Shell 脚本条件判断详解

条件判断是 Shell 脚本流程控制的基石。本文系统整理 if 语句的基本语法、[ ] 中的各类测试条件,以及 (( ))[[ ]] 这两种高级写法,并附上常见用法示例。

基本语法

if [ command ]; then
    符合该条件执行的语句
fi

扩展语法

if [ command ]; then
    符合该条件执行的语句
elif [ command ]; then
    符合该条件执行的语句
else
    符合该条件执行的语句
fi

语法说明

Shell 会按顺序执行 if 中的判断命令,如果命令执行后的返回状态是 0,则执行 then 后面的语句,否则跳过,继续后面的判断。

当有多个 elif 分支时,只有第一个返回 0 退出状态的分支会导致对应语句被执行;如果所有条件都不为 0,则执行 else 中的语句。

注意事项:

  1. [ ] 表示条件测试,空格很重要:[ 后面和 ] 前面都必须要有空格。
  2. thenfi 是分开的语句,如果要写在同一行,需要用分号隔开,如 if [ ... ]; then
  3. if 判断中对变量要加双引号,以免不必要的错误。不加双引号时,含空格的字符串变量判断会出问题,比如 [ -n "$var" ],如果 var 为空且不加引号会出错。
  4. [ ] 中的数值判断不支持浮点值。
  5. 如果单独使用 >< 号,系统会认为是输出或输入重定向,虽然有时结果显示正确,但其实是不对的,因此要对这些符号进行转义(或改用 (( )))。
  6. 默认情况下,运行 if 语句中的命令所产生的错误信息仍然会出现在脚本的输出结果中。
  7. 使用 -z-n 检查字符串长度时,未定义的变量也按长度为 0 处理。
  8. 空变量和未初始化的变量可能对脚本测试产生灾难性的影响,因此在不确定变量内容时,先用 -n-z 测试一下。
  9. $? 变量包含了上一条命令的退出状态(最近完成的前台进程),可用于检测退出状态。

常用参数

文件/目录判断

写法 说明
[ -a FILE ] FILE 存在则为真
[ -b FILE ] FILE 存在且是一个块设备文件则为真
[ -c FILE ] FILE 存在且是一个字符设备文件则为真
[ -d FILE ] FILE 存在且是一个目录则为真
[ -e FILE ] 指定的文件或目录存在时为真
[ -f FILE ] FILE 存在且是一个普通文件则为真
[ -g FILE ] FILE 存在且设置了 SGID 则为真
[ -h FILE ] FILE 存在且是一个符号链接则为真(该选项在一些老系统上无效)
[ -k FILE ] FILE 存在且设置了粘滞位则为真
[ -p FILE ] FILE 存在且是命令管道则为真
[ -r FILE ] FILE 存在且可读则为真
[ -s FILE ] FILE 存在且大小非 0 则为真
[ -u FILE ] FILE 存在且设置了 SUID 位则为真
[ -w FILE ] FILE 存在且可写则为真(目录要被写入内容,还必须是可执行的)
[ -x FILE ] FILE 存在且可执行则为真
[ -O FILE ] FILE 存在且属主为有效用户 ID 则为真
[ -G FILE ] FILE 存在且默认组为当前组则为真(只检查系统默认组)
[ -L FILE ] FILE 存在且是一个符号链接则为真
[ -N FILE ] FILE 存在且自上次读取后被修改过则为真
[ -S FILE ] FILE 存在且是一个套接字则为真
[ FILE1 -nt FILE2 ] FILE1 比 FILE2 新,或者 FILE1 存在但 FILE2 不存在则为真
[ FILE1 -ot FILE2 ] FILE1 比 FILE2 老,或者 FILE2 存在但 FILE1 不存在则为真
[ FILE1 -ef FILE2 ] FILE1 和 FILE2 指向相同的设备和节点号则为真

字符串判断

写法 说明
[ -z STRING ] STRING 长度为零则为真(即空为真)
[ -n STRING ] STRING 长度非零则为真(即非空为真)
[ STRING1 ] 字符串不为空则为真,与 -n 类似
[ STRING1 == STRING2 ] 两个字符串相同则为真
[ STRING1 != STRING2 ] 两个字符串不相同则为真
[ STRING1 \< STRING2 ] STRING1 字典排序在 STRING2 前面则为真
[ STRING1 \> STRING2 ] STRING1 字典排序在 STRING2 后面则为真

数值判断

写法 说明
[ INT1 -eq INT2 ] 两数相等则为真(=)
[ INT1 -ne INT2 ] 两数不等则为真(<>)
[ INT1 -gt INT2 ] INT1 大于 INT2 则为真(>)
[ INT1 -ge INT2 ] INT1 大于等于 INT2 则为真(>=)
[ INT1 -lt INT2 ] INT1 小于 INT2 则为真(<)
[ INT1 -le INT2 ] INT1 小于等于 INT2 则为真(<=)

逻辑判断

写法 说明
[ ! EXPR ] 逻辑非,EXPR 为 false 则为真
[ EXPR1 -a EXPR2 ] 逻辑与,EXPR1 和 EXPR2 全真则为真
[ EXPR1 -o EXPR2 ] 逻辑或,EXPR1 或 EXPR2 为真则为真
[ ] || [ ] 用 OR 合并两个条件
[ ] && [ ] 用 AND 合并两个条件

其他判断

  • [ -t FD ]:如果文件描述符 FD(默认值为 1)打开且指向一个终端则为真。
  • [ -o optionname ]:如果 Shell 选项 optionname 开启则为真。

IF 高级特性

双圆括号 (( )):表示数学表达式。普通的 [ ] 判断中只允许进行简单的算术比较,而双圆括号提供了更多的数学运算符,并且其中的 >< 号不需要转义。

双方括号 [[ ]]:表示高级字符串处理。双方括号中的判断使用标准的字符串比较,还可以使用匹配模式,从而支持与字符串相匹配的正则表达式。

双括号的作用举例:在 Shell 中,[ $a != 1 || $b = 2 ] 是不允许的,要写成 [ $a != 1 ] || [ $b = 2 ],而双括号就可以解决这个问题:[[ $a != 1 || $b = 2 ]]。又比如 [ "$a" -lt "$b" ],也可以改成双括号的形式 (( "$a" < "$b" ))

实例

1:判断目录 $doiido 是否存在,若不存在,则新建一个

if [ ! -d "$doiido" ]; then
    mkdir "$doiido"
fi

2:判断普通文件 $doiido 是否存在,若不存在,则新建一个

if [ ! -f "$doiido" ]; then
    touch "$doiido"
fi

3:判断 $doiido 是否存在并且是否具有可执行权限

if [ ! -x "$doiido" ]; then
    mkdir "$doiido"
    chmod +x "$doiido"
fi

4:判断变量 $doiido 是否有值

if [ ! -n "$doiido" ]; then
    echo "$doiido is empty"
    exit 0
fi

5:两个变量判断是否相等

if [ "$var1" = "$var2" ]; then
    echo '$var1 eq $var2'
else
    echo '$var1 not eq $var2'
fi

6:测试退出状态

if [ $? -eq 0 ]; then
    echo 'That is ok'
fi

7:数值的比较

if [ "$num" -gt "150" ]; then
    echo "$num is bigger than 150"
fi

8:a > b 且 a < c

(( a > b )) && (( a < c ))
[[ $a > $b ]] && [[ $a < $c ]]
[ $a -gt $b -a $a -lt $c ]

三种写法等价,其中前两种借助双括号,可读性更好,也更推荐。


   转载规则


《Shell 脚本条件判断详解》 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Shell 快捷键速查 Shell 快捷键速查
在终端里熟练使用快捷键,能大幅减少手在键盘和鼠标之间来回移动的时间。本文按功能对 Bash 中常用的 Ctrl 快捷键进行分组整理,方便速查。特别提醒:Ctrl+D 在不同语境下有两种完全不同的含义,文末会单独说明。 光标移动 快捷键
2020-11-01
下一篇 
Shell 基础语法:变量、条件、循环与函数 Shell 基础语法:变量、条件、循环与函数
Shell 脚本是 Linux 运维和日常开发中最常用的自动化工具。本文整理 Shell 最基础的语法知识:变量的定义与引用、字符串操作、条件判断、循环、函数,以及一个脚本究竟有哪几种执行方式、它们之间有什么区别。 变量的定义与引用变量名由
2020-10-18
  目录