本章介绍 Shell 脚本的条件判断语法。
if
是最常用的条件判断结构,只有符合给定条件时,才会执行指定的命令。它的语法如下。
1 | if commands |
注意:
elif
和else
结构并不是必须的,且elif
结构可以有多个。if
和then
写在同一行时,需要分号分隔。写成两行时不需要分号。if
结构也可以写成单行:if true; then echo 'hello world'; fi
if
关键字后面也可以是一条命令,该条命令执行成功(返回值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 |