7.1. if 语句简介

7.1.1. 概述

有时,您需要指定在 shell 脚本中采取的不同操作方案,具体取决于命令的成功或失败。if 结构允许您指定此类条件。

if 命令最紧凑的语法是

if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi

TEST-COMMAND 列表会被执行,如果其返回状态为零,则 CONSEQUENT-COMMANDS 列表会被执行。返回状态是最后执行的命令的退出状态,如果未测试到真条件,则为零。

TEST-COMMAND 通常涉及数值或字符串比较测试,但它也可以是任何命令,当成功时返回状态零,失败时返回其他状态。一元表达式通常用于检查文件的状态。如果FILE主参数之一的参数形式为/dev/fd/N,则会检查文件描述符 "N"stdin, stdoutstderr及其各自的文件描述符也可以用于测试。

7.1.1.1. if 语句中使用的表达式

下表概述了构成 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 ]如果FILE1FILE2更新,或者如果FILE1存在且FILE2不存在,则为真。
[ FILE1 -ot FILE2 ]如果FILE1FILE2旧,或者FILE2存在且FILE1不存在,则为真。
[ FILE1 -ef FILE2 ]如果FILE1FILE2引用相同的设备和 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 ]如果 EXPR1EXPR2 都为真,则为真。
[ EXPR1 -o EXPR2 ]如果 EXPR1EXPR2 中任一为真,则为真。

[(或 test)内建命令使用基于参数数量的一组规则来评估条件表达式。有关此主题的更多信息,请参阅 Bash 文档。就像 iffi 结尾一样,左方括号应在列出条件后关闭。

7.1.1.2. then 语句后的命令

跟随 then 语句的 CONSEQUENT-COMMANDS 列表可以是任何有效的 UNIX 命令、任何可执行程序、任何可执行的 shell 脚本或任何 shell 语句,但关闭 fi 除外。重要的是要记住 thenfi 在 shell 中被认为是分隔的语句。因此,当在命令行上发出时,它们以分号分隔。

在脚本中,if 语句的不同部分通常分隔良好。下面是一些简单的示例。

7.1.1.3. 检查文件

第一个示例检查文件的存在性

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.

7.1.1.4. 检查 shell 选项

添加到您的 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

Note环境
 

上面的示例在命令行输入时有效

anny ~> if [ -o noclobber ] ; then echo ; echo "your files are protected
against overwriting." ; echo ; fi

your files are protected against overwriting.

anny ~>

但是,如果您使用依赖于环境的条件测试,则在脚本中输入相同的命令时,您可能会得到不同的结果,因为脚本将打开一个新的 shell,其中预期的变量和选项可能不会自动设置。

7.1.2. if 语句的简单应用

7.1.2.1. 测试退出状态

变量?保存先前执行的命令(最近完成的前台进程)的退出状态。

以下示例显示了一个简单的测试

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 >

7.1.2.2. 数值比较

以下示例使用数值比较

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

7.1.2.3. 字符串比较

比较字符串以测试用户 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 >

Note真正的程序员
 

大多数程序员更喜欢使用 test 内建命令,它等效于使用方括号进行比较,如下所示

test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)

Note没有退出?
 

如果您在子 shell 中调用 exit,它不会将变量传递给父 shell。如果您不希望 Bash fork 一个子 shell,请使用 { 和 } 代替 ( 和 )。

有关使用 "(( EXPRESSION ))""[[ EXPRESSION ]]" 结构进行模式匹配的更多信息,请参阅 Bash 信息页。