1 变量

1.1 变量概论

  1. 变量名:

    • 只包含字母、数字和下划线: 变量名可以包含字母(大小写敏感)、数字和下划线 _,不能包含其他特殊字符。
    • 不能以数字开头: 变量名不能以数字开头,但可以包含数字。
    • 避免使用 Shell 关键字: 不要使用Shell的关键字(例如 if、then、else、fi、for、while 等)作为变量名,以免引起混淆。
    • 使用大写字母表示常量: 习惯上,常量的变量名通常使用大写字母,例如 PI=3.14。
    • 避免使用特殊符号: 尽量避免在变量名中使用特殊符号,因为它们可能与 Shell 的语法产生冲突。
    • 避免使用空格: 变量名中不应该包含空格,因为空格通常用于分隔命令和参数。
  2. 变量引用:$variable_name

  3. 变量边界:{variable_name}(尽量使用)

  4. 只读变量:readonly variable_name

  5. 删除变量:unset variable_name

  6. 变量类型 :
    运行shell时,会同时存在三种变量:

    • 局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
    • 环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
    • shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

1.2 字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。

1.2.1 定义

  1. 单引号
    特点:

    • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
    • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
  2. 双引号

    特点:

    • 双引号里可以有变量
    • 双引号里可以出现转义字符 
      注意:在shell脚本中,命令参数可以加双引号进行字符拼接,也可以不加双引号;区别在于空格是否会被认为参数分割符
  3. 反引号 
    特点:

    • 反引号里面可以写命令(会执行)
  4. 不用引号
    特点:

    • 方便
    • 可以有变量

1.2.2 一些例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1. index查找子字符位置
##查找字符 i 或 o 的位置(哪个字母先出现就计算哪个): 
string="runoob is a great site" 
echo `expr index "$string" io`  # 输出 4 

#2. 拼接字符串 
b=c
"abcd${b}" #输出 abcdc

#3. ${#}获取字符串长度 
string="abcd" echo ${#string} #输出 4 

#4. 字符串n:n提取子字符串 
string="runoob is a great site" 
echo ${string:1:4} # 输出 unoo 
echo –e 开启字符转义 

2 比较测试

2.1 条件测试

test condition(与其他语言不同的是shell编程中,0表示真,1表示假)

1
test -d dirname # 测试是否是目录

2.2 文件测试

文件测试,主要是判断文件是否符合指定的文件属性或文件是否存在

操作符 意义
-b 当文件存在,且为块文件时测试为 
-c 当文件存在,且为字符文件时测试 
为真
-d 当路径存在,且为目录时测试为真
-e 当文件或目录存在时为真
-f 当文件存在,且为普通文件时为真
-g 当文件或目录存在,且设置了SGID 
时为真
-h 当文件存在,且为符号链接时为真
-k 当文件或目录存在且设置了黏滞位 
时为真
-p 当文件存在,且为命名管道时为真
-r 当文件或目录存在,且可读时为真
-S 当文件存在,且其大小大于0时为真
操作符 意义
-S 当文件存在,且为套接字文件时为真
-t 当文件是与终端设备相关联的文件描述符时为真
-u 当文件或目录存在,且设置了SUID时为真
-w 当文件或目录存在,且可写时为真
-X 当文件或目录存在,且可执行时为真
当文件或目录存在,且被前进程有效用户时为真
-G 当文件或目录存在,且属于当前用户所在的用户 组时为真
file1ntfile2 nt是new than的缩写,所以file1比file2新时为真
file1otfile2 ot是old than的缩写,所以file1比file2旧时为真
file1effile2 ef是equal from的缩写,所以当file1和file2为同 文件的硬链接是为真

例如:

[ -b /dev/doc ];echo $? # 测试/dev/doc是否为块文件,并打印返回值
(一定要注意中括号与变量之间要留有空格)

2.3 字符串测试

操作符 意义
< 按ASCII码顺序,前面的字符串小于后面的字符串则为真
> 按ASCII码顺序,前面的字符串大于后面的字符串则为真
= 字符串完全相同为真;适用于[]和test(test和[]是等价的)
== 字符串完全相同为真;适用于[[]]
=~ 前面的字符串中包含后面的字符串时为真
!= 字符串不相等为真
-Z 字符串为空则为真
-n 字符串非空为真

【注意】

  • 以上左右操作符,在使用时,必须两侧都有空格才可以, 否则shell会将其中的操作符当做普通字符进行处理
  • 字符串测试最好用双中括号[[]]

2.4 整数值测试

Shell中对整数值的测试使用的是字母操作符,而不是通常的算术运算符,常用的整数值测试操作符如下表所示:

操作符 意义
-eq equal to,两个数相等为真
-ge greater than or equal to,前者大于等于后者为真
-gt greater than,前者大于后者为真
-le less than or equal to,前者小于等于后者为真
-It less than,前者小于后者为真
-ne no equal,两者不相等为真
附:
在 Bash 中,双方括号 [[ … ]] 用于条件测试。它提供了一些在单方括号 [ … ] 中不可用的高级条件测试功能,并且在使用时不需要进行字符串引号转义。双方括号通常用于更复杂的条件表达式。

3 语句

3.1 if语句

3.1.1 if……elif……else……(fi)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if [ 条件测试 ];then 

# 条件测试为真时执行该语句· 

elif [ 条件测试 ];then 

# 条件测试为真时执行该语句 

else 

# 上述条件测试都为假时执行该语句 

fi(shell语句没有程序块的概念,用fi表示结束) 

注意:[]可以改成(()),这样就可以用运算符比较了。

3.1.2 if……else……(fi)

1
2
3
4
5
6
7
8
9
if [ 条件测试 ];then 

# 条件测试为真时执行该语句· 

else 

# 上述条件测试都为假时执行该语句 

fi(shell语句没有程序块的概念,用fi表示结束) 

注意:

  • 在shell中空格用来分割不同的命令和参数:

    • 注意if和[]之间的空格
    • 条件和[]之间也要严格空格
    • []中运算符号与变量也要严格空格隔开,否则会产生错误
  • else后面不需要引号和then

  • 用&&、||、!连接不同条件时,再加一个[]

【示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash  
# "#!俗称shebang,此处用于指定shell为bash"
if [ -e ~/.bashrc ];then 

echo "file exists"

else 

echo "file is not exists"

fi 

【运行结果】 

os@tedu:~$/file.sh运行程序 

file exists.显然结果是真,因此输出“file exists” 

3.2 case语句

case … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

3.3 for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 语法
for var in list 
do 
循环语句 
done 

# 举例: 

# 1)C风格 

for((i=1;i<=10;i++))   do    echo $(expr $i \* 3 + 1)   done   

# 2)seq风格 

for i in $(seq 1 10)   do    echo $(expr $i \* 3 + 1)  done 

# 3){1..50}风格 

for i in {1..10}   do   echo $(expr $i \* 3 + 1)   done 

3.4 while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while 表达式 
do  

循环语句 

done 
# done后面可以接重定向符号,将文件行做为输入判断: 

while 表达式 

do  

循环语句 

done < filename 

3.5 until循环

until循环结构在其他编程语言中并不常见。使用方法与前面讲的for循环和while循环结构类似,但是until是在条件测试为假时执行循环语句,直到条件测试为真时退出。

1
2
3
4
5
6
7
until 表达式 
do 

# 循环语句 

done 

3.6 select循环

select循环结构是Shell中比较特殊的一种循环结构,前面讲的循环结构中,大多有一个数组来保存各个元素,有时也不需要数组。而select循环结构中数组和变量时必须的,select循环是一个可以和用户进行交互式的循环结构,在命令行Shell中非常实用

1
2
3
4
5
6
select var in list 
do 

# 循环语句 

done 

shell中continue、break语句和在python、c用法一样

四、数组

4.1 定义数组

  • 数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似)。
  • 与大部分编程语言类似,数组元素的下标由 0 开始。 
    Shell 数组用括号来表示,元素用"空格"符号分割开(数组可以换行,换行符不影响),语法格式如下:
1
array_name=(value1 value2 ... valuen) 

4.2 访问数组:

1
2
3
4
5
6
7
8
9
10
a=(1 2 3 4 5) 

unset a[2] # 销毁指定数组元素 

unset a # 销毁数组

# '#'获取用户数组长度(使用 @ 或 * 可以获取数组中的所有元素)
${#array[@]} 

${#array[*]} 

注意:

  • shell中赋值符号与变量和值之间不能有空格,否则会被认为三个不同命令或参数
  • "#"操作符的作用位获取参数的长度,不仅可以获取数组的长度,对于变量也可以使用该操作符进行获取长度

4.4 数组切片

1
2
3
Array1=${array[@]:m:n} 

Array1=${array[*]:m:n} 

4.5 数组替换

1
2
3
4
5
6
7
8
9
10
${array[@]/from/to} 

# 举例
arr=(1 2 3 4 5)
${arr[@]/2/10} # 输出:1 10 3 4 5;但只是临时替换
arr=${arr[@]/2/10} # 重新赋值永久替换

# array需要替换的数组名称 
# from数组中需要被替换的旧字符串 
# to被替换的新的字符串,可以省略 

4.6 关联数组

除了常见的下标索引形式的数组,Shell中还有一种被称为关联数组的数组形式,借助散列技术,使用字符串作为索引值,而不是使用数组作为索引

1
ass array=([index]=vall [index1]=val2) 

4.7 遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
my_array=('Black' 'Copper' 'Crowntail' 'Dragon' 'Dumbo_Halfmoon'  

'Dumbo_HMPK' 'Fighter' 'Giant' 'Halfmoon' 'Imbellis' 'Koi' 'Mahachaiensis' 'Orange' 'Red' 'Royal' 'Siamorientalis' 'Smaragdina' 'Smaragdina_guitar' 'Splendens' 'Steel' 'Stiktos' 'Turquoise' 'Veiltail' 'White' 'Yellow') 

for value in ${my_array[*]} 
do 

in_file="${outdir}${value}_ID.txt" 

echo -e "${in_file}\n" 

done

五、函数

5.1 定义

1
2
3
4
5
function 函数名(){ 

# 函数体 


5.2 函数的参数

函数参数是函数调用时,用以传递给函数的一些数据,Shell中函数可以接受多个参数。除了传递进来的参数外,每个函数中还都包含了一些内部参数,常用获取内部参数使用的,常见的内部参数符号如下所示:

符号 意义
# 传递到脚本的参数个数
* 以一个单字符串显示所有向脚本传递的参数
$ 脚本运行是的档期那进程ID号
l 后台运行的最后一个进程ID号
@ 与*相同,但是使用时加引号,并在引号中返回每个参数
- 显示Shell使用的当前选项,与set命令功能相同
? 显示最后命令的退出状态

六、文本处理命令

6.1 格式化输出

6.1.1 语法

格式化输出能够对把输出的内容根据指定的格式输出,自动进行对齐、换行等操作。格式化输出内容使用的是printf指令,printf指令常用的格式化字符如下所示

1
2
3
4
5
6
# 语法
printf format-string [arguments...]
# 举例
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
# 输出
姓名 性别 体重kg

6.1.2 格式化字符

格式说明符由 % 字符开始,后跟一个或多个字符,用于指定输出的格式。常用的格式说明符包括:

  • %s:字符串
  • %d:十进制整数
  • %f:浮点数
  • %c:字符
  • %x:十六进制数
  • %o:八进制数
  • %b:二进制数
  • %e:科学计数法表示的浮点数

6.1.3 printf 的转义序列

序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

6.2 sed命令

sed是一个很好的文本处理工具,其本身是一个管道命令,主要是以行为单位进行文本文档的处理,可以将数据行进行替换、删除、新增、选取等特定工作。sed常用选项及功能如下所示:

1
2
# 语法
sed [-hnV][-e<script>][-f<script文件>][文本文件]
参数 作用
-h 显示帮助
-n 使用安静模式
-v 显示版本信息
-e 以选项中指定的script(命令行)来处理输入的文本文件
-f 以选项中指定的script(文件)来处理输入的文本文件
-r 执行正则表达式的语法
-i 直接修改读取的文件内容,而不是输出到终端
动作 作用
a 新增行,a的后面可以是字符串
c 取代行,c的后面可以是字符串
d 删除行,通常后面不接参数,直接删除对应行
i 插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p 打印,将某个选择的数据打印出,通常与-n选项一起运行
s 替换,直接或通过正则表达式进行替换

6.3 awk命令

作为一款文本处理工具,awk被设计用于数据流,其不仅能够对行进行操作,还可以对列进行操作。awk命令有许多内建的功能,比如数组、函数等,具有很大的灵活性。

1
2
3
4
5
6
7
8
9
10
# 【语法】 
awk ‘BEGIN(command}{command 1}END{command 2}‘file 

# command是脚本开始执行时执行的命令 

# command 1为脚本执行过程中执行的命令 

# command 2为脚本执行结束时执行的命令 

# file是需要被处理的文件名称 

6.4 diff命令

diff命令能够对两个文件进行详细的对比,并将差异结果进行重点标记,输出到文件中。输出到的文件成为被修补文件(patchfile),其中包含了修改过的、添加、删除的行及行号,用户可以通过修补文件对源文件进行更改。

1
2
3
4
# 【语法】 
diff ver1ver2 

# ver1和ver2是要对比的两个文件,对比结果会输出到终端中,也可以采用重定向的方式将输出保存到任意位置