这是用于在 if 命令测试为真时采取一种行动方案,而在测试为假时采取另一种行动方案的结构。一个例子
freddy scripts> gender="male" freddy scripts> if [[ "$gender" == "f*" ]] More input> then echo "Pleasure to meet you, Madame." More input> else echo "How come the lady hasn't got a drink yet?" More input> fi How come the lady hasn't got a drink yet? freddy scripts> |
![]() | [] 与 [[]] |
---|---|
与此相反的是[, [[防止变量值进行单词分割。所以,如果VAR="var with spaces",你不需要用双引号括起来$VAR在测试中 - 尽管使用引号仍然是一个好习惯。此外,[[防止路径名扩展,因此带有通配符的字面字符串不会尝试扩展为文件名。使用[[, ==和!=将右侧的字符串解释为要与左侧值匹配的 shell glob 模式,例如[[ "value" == val* ]]. |
与 then 语句后面的 CONSEQUENT-COMMANDS 列表一样,else 语句后面的 ALTERNATE-CONSEQUENT-COMMANDS 列表可以容纳任何返回退出状态的 UNIX 风格命令。
另一个例子,扩展了 第 7.1.2.1 节 中的例子
anny ~> su - Password: [root@elegance root]# if ! grep ^$USER /etc/passwd 1> /dev/null > then echo "your user account is not managed locally" > else echo "your account is managed from the local /etc/passwd file" > fi your account is managed from the local /etc/passwd file [root@elegance root]# |
我们切换到 root 帐户来演示 else 语句的效果 - 你的 root 通常是一个本地帐户,而你自己的用户帐户可能由中央系统(如 LDAP 服务器)管理。
与其设置一个变量然后执行脚本,通常更优雅的做法是将变量的值放在命令行上。
我们使用位置参数$1, $2, ..., $N为此目的。$#指的是命令行参数的数量。$0指的是脚本的名称。
以下是一个简单的例子
这是另一个例子,使用两个参数
anny ~> cat weight.sh #!/bin/bash # This script prints a message about your weight if you give it your # weight in kilos and height in centimeters. weight="$1" height="$2" idealweight=$[$height - 110] if [ $weight -le $idealweight ] ; then echo "You should eat a bit more fat." else echo "You should eat a bit more fruit." fi anny ~> bash -x weight.sh 55 169 + weight=55 + height=169 + idealweight=59 + '[' 55 -le 59 ']' + echo 'You should eat a bit more fat.' You should eat a bit more fat. |
下面的例子展示了如何更改之前的脚本,以便在给出多于或少于 2 个参数时打印消息
anny ~> cat weight.sh #!/bin/bash # This script prints a message about your weight if you give it your # weight in kilos and height in centimeters. if [ ! $# == 2 ]; then echo "Usage: $0 weight_in_kilos length_in_centimeters" exit fi weight="$1" height="$2" idealweight=$[$height - 110] if [ $weight -le $idealweight ] ; then echo "You should eat a bit more fat." else echo "You should eat a bit more fruit." fi anny ~> weight.sh 70 150 You should eat a bit more fruit. anny ~> weight.sh 70 150 33 Usage: ./weight.sh weight_in_kilos length_in_centimeters |
第一个参数被称为$1,第二个参数被称为$2等等。参数的总数存储在$#.
查看 第 7.2.5 节,了解更优雅的打印用法消息的方式。
这个测试在很多脚本中都会用到,因为如果你知道它们不会工作,那么启动很多程序是没有用的
#!/bin/bash # This script gives information about a file. FILENAME="$1" echo "Properties for $FILENAME:" if [ -f $FILENAME ]; then echo "Size is $(ls -lh $FILENAME | awk '{ print $5 }')" echo "Type is $(file $FILENAME | cut -d":" -f2 -)" echo "Inode number is $(ls -i $FILENAME | cut -d" " -f1 -)" echo "$(df -h $FILENAME | grep -v Mounted | awk '{ print "On",$1", \ which is mounted as the",$6,"partition."}')" else echo "File does not exist." fi |
请注意,文件是通过变量引用的;在本例中,它是脚本的第一个参数。或者,当没有给出参数时,文件位置通常存储在脚本开头的变量中,并且它们的内容通过这些变量引用。因此,当你想在脚本中更改文件名时,你只需要更改一次。
![]() | 带有空格的文件名 |
---|---|
如果$1的值可以解析为多个单词,则上述示例将失败。在这种情况下,if 命令可以通过在文件名周围使用双引号,或者通过使用[[而不是[. |
这是 if 语句的完整形式
if TEST-COMMANDS; then
CONSEQUENT-COMMANDS;
elif MORE-TEST-COMMANDS; then
MORE-CONSEQUENT-COMMANDS;
else ALTERNATE-CONSEQUENT-COMMANDS;
fi
TEST-COMMANDS 列表被执行,如果它的返回状态为零,则执行 CONSEQUENT-COMMANDS 列表。如果 TEST-COMMANDS 返回非零状态,则依次执行每个 elif 列表,如果其退出状态为零,则执行相应的 MORE-CONSEQUENT-COMMANDS,并且命令完成。如果 else 后跟一个 ALTERNATE-CONSEQUENT-COMMANDS 列表,并且最后一个 if 或 elif 子句中的最后一个命令具有非零退出状态,则执行 ALTERNATE-CONSEQUENT-COMMANDS。返回状态是最后执行的命令的退出状态,如果没有任何条件测试为真,则为零。
这是一个你可以放在 crontab 中每日执行的例子
anny /etc/cron.daily> cat disktest.sh #!/bin/bash # This script does a very simple test for checking disk space. space=`df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -` alertvalue="80" if [ "$space" -ge "$alertvalue" ]; then echo "At least one of my disks is nearly full!" | mail -s "daily diskcheck" root else echo "Disk space normal" | mail -s "daily diskcheck" root fi |
在 if 语句内部,你可以使用另一个 if 语句。你可以使用任意多层嵌套的 if 语句,只要你在逻辑上能够管理。
这是一个测试闰年的例子
anny ~/testdir> cat testleap.sh #!/bin/bash # This script will test if we're in a leap year or not. year=`date +%Y` if [ $[$year % 400] -eq "0" ]; then echo "This is a leap year. February has 29 days." elif [ $[$year % 4] -eq 0 ]; then if [ $[$year % 100] -ne 0 ]; then echo "This is a leap year, February has 29 days." else echo "This is not a leap year. February has 28 days." fi else echo "This is not a leap year. February has 28 days." fi anny ~/testdir> date Tue Jan 14 20:37:55 CET 2003 anny ~/testdir> testleap.sh This is not a leap year. |
上面的脚本可以使用布尔运算符 "AND" (&&) 和 "OR" (||) 缩短。
我们使用双括号来测试算术表达式,请参阅 第 3.4.6 节。这等同于 let 语句。如果你尝试类似 $[$year % 400] 的操作,你将会在方括号这里遇到困难,因为在这里,方括号本身并不代表一个实际的命令。
在其他编辑器中,gvim 是其中一个支持根据文件格式进行颜色方案的编辑器;这种编辑器对于检测代码中的错误很有用。
我们在 第 7.2.1.3 节 中已经简要地了解了 exit 语句。它终止整个脚本的执行。如果从用户请求的输入不正确,如果语句未成功运行,或者如果发生其他错误,则最常使用它。
exit 语句接受一个可选参数。此参数是整数退出状态代码,它被传递回父进程并存储在$?变量中。
零参数表示脚本成功运行。程序员可以使用任何其他值将不同的消息传递回父进程,以便可以根据子进程的失败或成功采取不同的操作。如果没有为 exit 命令提供参数,则父 shell 使用当前的$?变量中。
下面是一个稍作调整的penguin.sh脚本的示例,该脚本将其退出状态发送回父进程,feed.sh:
anny ~/testdir> cat penguin.sh #!/bin/bash # This script lets you present different menus to Tux. He will only be happy # when given a fish. We've also added a dolphin and (presumably) a camel. if [ "$menu" == "fish" ]; then if [ "$animal" == "penguin" ]; then echo "Hmmmmmm fish... Tux happy!" elif [ "$animal" == "dolphin" ]; then echo "Pweetpeettreetppeterdepweet!" else echo "*prrrrrrrt*" fi else if [ "$animal" == "penguin" ]; then echo "Tux don't like that. Tux wants fish!" exit 1 elif [ "$animal" == "dolphin" ]; then echo "Pweepwishpeeterdepweet!" exit 2 else echo "Will you read this sign?!" exit 3 fi fi |
此脚本在下一个脚本中被调用,因此导出其变量menu和animal:
anny ~/testdir> cat feed.sh #!/bin/bash # This script acts upon the exit status given by penguin.sh export menu="$1" export animal="$2" feed="/nethome/anny/testdir/penguin.sh" $feed $menu $animal case $? in 1) echo "Guard: You'd better give'm a fish, less they get violent..." ;; 2) echo "Guard: It's because of people like you that they are leaving earth all the time..." ;; 3) echo "Guard: Buy the food that the Zoo provides for the animals, you ***, how do you think we survive?" ;; *) echo "Guard: Don't forget the guide!" ;; esac anny ~/testdir> ./feed.sh apple penguin Tux don't like that. Tux wants fish! Guard: You'd better give'm a fish, less they get violent... |
正如你所看到的,退出状态代码可以自由选择。现有命令通常有一系列定义的代码;有关更多信息,请参阅每个命令的程序员手册。