有时,您需要指定在 shell 脚本中采取的不同操作方案,具体取决于命令的成功或失败。if 结构允许您指定此类条件。
if 命令最紧凑的语法是
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
TEST-COMMAND 列表会被执行,如果其返回状态为零,则 CONSEQUENT-COMMANDS 列表会被执行。返回状态是最后执行的命令的退出状态,如果未测试到真条件,则为零。
TEST-COMMAND 通常涉及数值或字符串比较测试,但它也可以是任何命令,当成功时返回状态零,失败时返回其他状态。一元表达式通常用于检查文件的状态。如果FILE主参数之一的参数形式为/dev/fd/N,则会检查文件描述符 "N"。stdin, stdout和stderr及其各自的文件描述符也可以用于测试。
下表概述了构成 TEST-COMMAND 命令或命令列表的所谓 "主参数"。这些主参数放在方括号之间,以指示条件表达式的测试。
表 7-1. 主表达式
主参数 | 含义 |
---|---|
[ -a FILE ] | 如果FILE存在,则为真。 |
[ -b FILE ] | 如果FILE存在且为块特殊文件,则为真。 |
[ -c FILE ] | 如果FILE存在且为字符特殊文件,则为真。 |
[ -d FILE ] | 如果FILE存在且为目录,则为真。 |
[ -e FILE ] | 如果FILE存在,则为真。 |
[ -f FILE ] | 如果FILE存在且为常规文件,则为真。 |
[ -g FILE ] | 如果FILE存在且其 SGID 位已设置,则为真。 |
[ -h FILE ] | 如果FILE存在且为符号链接,则为真。 |
[ -k FILE ] | 如果FILE存在且其粘滞位已设置,则为真。 |
[ -p FILE ] | 如果FILE存在且为命名管道 (FIFO),则为真。 |
[ -r FILE ] | 如果FILE存在且可读,则为真。 |
[ -s FILE ] | 如果FILE存在且大小大于零,则为真。 |
[ -t FD ] | 如果文件描述符FD已打开且引用终端,则为真。 |
[ -u FILE ] | 如果FILE存在且其 SUID(设置用户 ID)位已设置,则为真。 |
[ -w FILE ] | 如果FILE存在且可写,则为真。 |
[ -x FILE ] | 如果FILE存在且可执行,则为真。 |
[ -O FILE ] | 如果FILE存在且归有效用户 ID 所有,则为真。 |
[ -G FILE ] | 如果FILE存在且归有效组 ID 所有,则为真。 |
[ -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引用相同的设备和 inode 编号,则为真。 |
[ -oOPTIONNAME ] | 如果 shell 选项 "OPTIONNAME" 已启用,则为真。 |
[ -zSTRING ] | 如果 "STRING" 的长度为零,则为真。 |
[ -nSTRING ] 或 [ STRING ] | 如果 "STRING" 的长度非零,则为真。 |
[ STRING1 == STRING2 ] | 如果字符串相等,则为真。 为了严格遵守 POSIX 标准,可以使用 "=" 代替 "=="。 |
[ STRING1 != STRING2 ] | 如果字符串不相等,则为真。 |
[ STRING1 < STRING2 ] | 如果 "STRING1" 在当前区域设置中按字典顺序排在 "STRING2" 之前,则为真。 |
[ STRING1 > STRING2 ] | 如果 "STRING1" 在当前区域设置中按字典顺序排在 "STRING2" 之后,则为真。 |
[ ARG1 OP ARG2 ] | "OP" 是以下之一-eq, -ne, -lt, -le, -gt或-ge。 这些算术二元运算符在 "ARG1" 分别等于、不等于、小于、小于或等于、大于或大于或等于 "ARG2" 时返回真。"ARG1" 和 "ARG2" 是整数。 |
表达式可以使用以下运算符组合,运算符按优先级降序排列
表 7-2. 组合表达式
操作 | 效果 |
---|---|
[ ! EXPR ] | 如果 EXPR 为假,则为真。 |
[ ( EXPR ) ] | 返回 EXPR 的值。这可以用于覆盖运算符的正常优先级。 |
[ EXPR1 -a EXPR2 ] | 如果 EXPR1 和 EXPR2 都为真,则为真。 |
[ EXPR1 -o EXPR2 ] | 如果 EXPR1 或 EXPR2 中任一为真,则为真。 |
[(或 test)内建命令使用基于参数数量的一组规则来评估条件表达式。有关此主题的更多信息,请参阅 Bash 文档。就像 if 以 fi 结尾一样,左方括号应在列出条件后关闭。
跟随 then 语句的 CONSEQUENT-COMMANDS 列表可以是任何有效的 UNIX 命令、任何可执行程序、任何可执行的 shell 脚本或任何 shell 语句,但关闭 fi 除外。重要的是要记住 then 和 fi 在 shell 中被认为是分隔的语句。因此,当在命令行上发出时,它们以分号分隔。
在脚本中,if 语句的不同部分通常分隔良好。下面是一些简单的示例。
第一个示例检查文件的存在性
anny ~> cat msgcheck.sh #!/bin/bash echo "This scripts checks the existence of the messages file." echo "Checking..." if [ -f /var/log/messages ] then echo "/var/log/messages exists." fi echo echo "...done." anny ~> ./msgcheck.sh This scripts checks the existence of the messages file. Checking... /var/log/messages exists. ...done. |
添加到您的 Bash 配置文件中
# These lines will print a message if the noclobber option is set: if [ -o noclobber ] then echo "Your files are protected against accidental overwriting using redirection." fi |
![]() | 环境 | |
---|---|---|
上面的示例在命令行输入时有效
但是,如果您使用依赖于环境的条件测试,则在脚本中输入相同的命令时,您可能会得到不同的结果,因为脚本将打开一个新的 shell,其中预期的变量和选项可能不会自动设置。 |
变量?保存先前执行的命令(最近完成的前台进程)的退出状态。
以下示例显示了一个简单的测试
anny ~> if [ $? -eq 0 ] More input> then echo 'That was a good job!' More input> fi That was a good job! anny ~> |
以下示例演示了 TEST-COMMANDS 可能是任何返回退出状态的 UNIX 命令,并且 if 再次返回零的退出状态
anny ~> if ! grep $USER /etc/passwd More input> then echo "your user account is not managed locally"; fi your user account is not managed locally anny > echo $? 0 anny > |
可以按如下方式获得相同的结果
anny > grep $USER /etc/passwd anny > if [ $? -ne 0 ] ; then echo "not a local account" ; fi not a local account anny > |
以下示例使用数值比较
anny > num=`wc -l work.txt` anny > echo $num 201 anny > if [ "$num" -gt "150" ] More input> then echo ; echo "you've worked hard enough for today." More input> echo ; fi you've worked hard enough for today. anny > |
此脚本在每个星期日由 cron 执行。如果周数是偶数,它会提醒您拿出垃圾桶
#!/bin/bash # Calculate the week number using the date command: WEEKOFFSET=$[ $(date +"%V") % 2 ] # Test if we have a remainder. If not, this is an even week so send a message. # Else, do nothing. if [ $WEEKOFFSET -eq "0" ]; then echo "Sunday evening, put out the garbage cans." | mail -s "Garbage cans out" your@your_domain.org fi |
比较字符串以测试用户 ID 的示例
if [ "$(whoami)" != 'root' ]; then echo "You have no permission to run $0 as non-root user." exit 1; fi |
使用 Bash,您可以缩短这种类型的结构。上述测试的紧凑等效形式如下
[ "$(whoami)" != 'root' ] && ( echo you are using a non-privileged account; exit 1 ) |
类似于 "&&" 表达式,它指示如果测试证明为真该怎么做,"||" 指定如果测试为假该怎么做。
正则表达式也可以用于比较
anny > gender="female" anny > if [[ "$gender" == f* ]] More input> then echo "Pleasure to meet you, Madame."; fi Pleasure to meet you, Madame. anny > |
![]() | 真正的程序员 | |
---|---|---|
大多数程序员更喜欢使用 test 内建命令,它等效于使用方括号进行比较,如下所示
|
![]() | 没有退出? |
---|---|
如果您在子 shell 中调用 exit,它不会将变量传递给父 shell。如果您不希望 Bash fork 一个子 shell,请使用 { 和 } 代替 ( 和 )。 |
有关使用 "(( EXPRESSION ))" 和 "[[ EXPRESSION ]]" 结构进行模式匹配的更多信息,请参阅 Bash 信息页。