本章介绍 Shell 脚本的条件判断语法。
if是最常用的条件判断结构,只有符合给定条件时,才会执行指定的命令。它的语法如下。
1 | if commands |
注意:
elif和else结构并不是必须的,且elif结构可以有多个。if和then写在同一行时,需要分号分隔。写成两行时不需要分号。if结构也可以写成单行:if true; then echo 'hello world'; fiif关键字后面也可以是一条命令,该条命令执行成功(返回值0),就意味着判断条件成立。if后面可以跟任意数量的命令。这时,所有命令都会执行,但是判断真伪只看最后一个命令的执行结果。if关键字后面,跟的是一个命令。这个命令可以是test命令,也可以是其他命令。命令的返回值为0表示判断成立,否则表示不成立。
下面是test命令的形式。
1 | # 写法一 |
expression是一个表达式。这个表达式为真,test命令执行成功(返回值为0);表达式为伪,test命令执行失败(返回值为1)。
test命令中常用的判断表达式有下面这些
以下常用表达式用来判断文件状态。
| 表达式 | 说明 |
|---|---|
[ -e file ] |
exist 检测文件(包括目录)是否存在,如果是,则返回 true。 |
[ -d file ] |
检测文件是否是目录,如果是,则返回 true。 |
[ -f file ] |
检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 |
[ -r file ] |
检测文件是否可读,如果是,则返回 true。 |
[ -w file ] |
检测文件是否可写,如果是,则返回 true。 |
[ -x file ] |
检测文件是否可执行,如果是,则返回 true。 |
[ -s file ] |
space 检测文件是否为空(文件大小是否大于0),不为空返回 true。 |
注意:如果表达式中的file用变量$FILE来代替,那么表达式中的$FILE要放在双引号之中,这样可以防止变量$FILE为空,从而出错。因为$FILE如果为空,这时[ -e $FILE ]就变成[ -e ],这会被判断为真。而$FILE放在双引号之中,[ -e "$FILE" ]就变成[ -e "" ],这会被判断为伪。
以下表达式用来判断字符串。
| 表达式 | 说明 |
|---|---|
[ -n string ] |
如果字符串string的长度大于零,则判断为真 |
[ -z string ] |
如果字符串string的长度为零,则判断为真 |
[ string1 = string2 ] |
如果string1和string2相同,则判断为真 |
[ string1 == string2 ] |
等同于[ string1 = string2 ] |
[ string1 != string2 ] |
如果string1和string2不相同,则判断为真 |
[ string1 '>' string2 ] |
如果按照字典顺序string1排列在string2之后,则判断为真 |
[ string1 '<' string2 ] |
如果按照字典顺序string1排列在string2之前,则判断为真 |
注意,test命令内部的>和<,必须用引号引起来(或者是用反斜杠转义)。否则,它们会被 shell 解释为重定向操作符。
下面的表达式用于判断整数。
| 运算符 | 说明 |
|---|---|
[ integer1 -eq integer2 ] |
equal 如果两个数相等,则返回 true。 |
[ integer1 -ne integer2 ] |
not equal 如果两个数不相等,则返回true。 |
[ integer1 -gt integer2 ] |
greater than 如果前者大于后者,则返回true。 |
[ integer1 -ge integer2 ] |
greater than or equal to 如果前者大于等于后者,则返回true |
[ integer1 -lt integer2 ] |
less than 如果前者小于后者,则返回true |
[ integer1 -le integer2 ] |
less than or equal to 如果前者小于等于后者,则返回true |
[[ expression ]]这种判断形式,支持正则表达式。
1 | [[ string1 =~ regex ]] |
上面的语法中,regex是一个正则表示式,=~是正则比较运算符。
下面是一个例子。
1 |
|
上面代码中,先判断变量INT的字符串形式,是否满足^-?[0-9]+$的正则模式,如果满足就表明它是一个整数。
通过逻辑判断,可以把多个test判断表达式结合起来,创造更复杂的判断。三种逻辑运算AND,OR,和NOT,都有自己的专用符号。
AND运算:符号&&,也可使用参数-a。比如:if [[ $INT -ge $MIN_VAL && $INT -le $MAX_VAL ]]OR运算:符号||,也可使用参数-o。NOT运算:符号!。注意:使用否定操作符!时,最好用圆括号确定否定的范围。并且test命令内部使用的圆括号,必须使用引号或者转义,否则会被 Bash 解释。
命令执行结束后,会有一个返回值。0表示执行成功,非0(通常是1)表示执行失败。环境变量$?可以读取前一个命令的返回值。
利用这一点,可以在脚本中对命令执行结果进行判断。
1 | if cd /path/to/somewhere; then |
需要注意的是,对命令的执行结果进行判断,虽然返回值是0,认为满足条件,这与对整数0进行判断不同。
如果if结构使用的不是test命令,而是普通命令,比如上一节的((...))算术运算,或者test命令与普通命令混用,那么可以使用 Bash 的命令控制操作符&&(AND)和||(OR),进行多个命令的逻辑运算。
1 | $ command1 && command2 # 先执行command1,只有command1执行成功后, 才会执行command2 |
下面是一些普通命令使用逻辑运算的例子
1 | # 创建一个名为temp的目录,执行成功后,才会执行第二个命令,进入这个目录 |
下面是if与&&结合使用的写法的示例
1 |
|
Bash 还提供了((...))作为算术条件,进行算术运算的判断。
只要是算术表达式,都能用于((...))语法,详见《Bash 的算术运算》一章。
注意:
test命令,而是直接使用((...))结构。这个结构的返回值,决定了判断的真伪。((...))也可以用于变量赋值。(( foo = 5 ))返回5判断为真,(( foo = 0 ))返回0判断为假。用法
case结构用于多值判断,可以为每个值指定对应的命令,跟包含多个elif的if结构等价,但是语义更好。case结构如下:
1 | case expression in |
上面代码中,expression是一个表达式,pattern是表达式的值或者一个模式,后面跟半个圆括号),可以有多条,每条以两个分号(;;)结尾。esac表示退出case结构。下面是一个简单的例子:
1 |
|
通配符
case的匹配模式可以使用各种通配符
a):匹配a。a|b):匹配a或b。[[:alpha:]]):匹配单个字母。???):匹配3个字符的单词。*.txt):匹配.txt结尾。*):匹配任意输入,通过作为case结构的最后一个模式。比如下面的例子:
1 |
|
匹配多条件
Bash 4.0之前,case结构匹配一个条件之后就会退出case结构。Bash 4.0之后,允许匹配多个条件,这时可以用;;&终止每个条件块。
1 |
|
执行上面的脚本,会得到下面的结果。
1 | $ test.sh |