一、if-then语句
shell中最基本的结构化命令是if-then语句:
if command
then
commands
fi
如果你在用其他编程语言的if-then语句,这种形式可能会让你有点困惑。
在其他编程语言中,if语句之后的对象是一个等式,这个等式的求值结果为TRUE或FALSE。
但bash shell的if语句并不是这么做的。bash shell的if语句会运行if后面的那个命令。
如果该命令的退出状态码(参见第11章)是0(该命令成功运行),位于then部分的命令就会被执行。
如果该命令的退出状态码是其他值,then部分的命令就不会被执行,bash shell会继续执行脚本中的下一个命令。
fi语句用来表示if-then语句到此结束。
例如:
#!/bin/bash
# testing the if statement
if pwd
then
echo "It worked"
fi
pwd命令成功运行,屏幕会打印“It worked”。
否则:
#!/bin/bash
# testing a bad command
if IamNotaCommand
then
echo "It worked"
fi
echo "We are outside the if statement"
乱码的命令不执行,返回正数,为错误值,不执行then后的语句,因此输出“We are outside the if statement”。
二、if-then-else语句
在if-then语句中,不管命令是否成功执行,你都只有一种选择。如果命令返回一个非零退出状态码,bash shell会继续执行脚本中的下一条命令。在这种情况下,如果能够执行另一组命令就好了。这正是if-then-else语句的作用:
if command
then
commands
else
commands
fi
例如:
#!/bin/bash
# testing the else section
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The bash files for user $testuser are:"
ls -a /home/$testuser/.b*
else
echo "The user $testuser does not exist on this system."
fi
if语句行使用grep命令在/etc/passwd文件中查找某个用户名当前是否在系统上使用。如果有
用户使用了那个登录名,脚本会显示一些文本信息并列出该用户HOME目录的bash文件。
显然“NoSuchUser”并不是我的用户名,因此执行else后的内容,输出“The user NoSuchUser does not exist on this system”。
三、嵌套if-then
有时你需要检查脚本代码中的多种条件。对此,可以使用嵌套的if-then语句。
要检查/etc/passwd文件中是否存在某个用户名以及该用户的目录是否尚在,可以使用嵌套的
if-then语句。嵌套的if-then语句位于主if-then-else语句的else代码块中。
例如,要检查/etc/passwd文件中是否存在某个用户名以及该用户的目录是否尚在,可以使用嵌套的if-then语句。嵌套的if-then语句位于主if-then-else语句的else代码块中。
#!/bin/bash
# Testing nested ifs
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
else
echo "The user $testuser does not exist on this system."
if ls -d /home/$testuser/
then
echo "However, $testuser has a directory."
fi
fi
这个脚本准确无误地发现,尽管登录名已经从/etc/passwd中删除了,但是该用户的目录仍然存在。
在脚本中使用这种嵌套if-then语句的问题在于代码不易阅读,很难理清逻辑流程。
可以使用else部分的另一种形式:elif。
这样就不用再书写多个if-then语句了。elif使用另一个if-then语句延续else部分。
if command1
then
commands
elif command2
then
more commands
fi
elif语句行提供了另一个要测试的命令,这类似于原始的if语句行。如果elif后命令的退出状态码是0,则bash会执行第二个then语句部分的命令。使用这种嵌套方法,代码更清晰,逻辑更易懂。
更进一步,让脚本检查拥有目录的不存在用户以及没有拥有目录的不存在用户:
#!/bin/bash
# Testing nested ifs - use elif & else
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
#
elif ls -d /home/$testuser
then
echo "The user $testuser does not exist on this system."
echo "However, $testuser has a directory."
#
else
echo "The user $testuser does not exist on this system."
echo "And, $testuser does not have a directory."
fi
这段代码检查所给用户是否在此系统中,如果有,则输出“The user $testuser exists on this system”,并列出其目录;
如果没有但登录过,则输出“The user $testuser does not exist on this system,However, $testuser has a directory”;
如果这个用户从没在服务器存在过,则输出“The user $testuser does not exist on this system,And, $testuser does not have a directory”。
四、test语句
if-then语句有一个限制,那就是它只能检测退出码。因此,我们需要另一个命令——test,来检测退出码之外的条件。
test condition
与if-then联用时,它是这样的:
if test condition
then
commands
fi
如果不写test命令的condition部分,它会以非零的退出状态码退出,并执行else语句块。
另外,bash shell提供了“[ ]”,无需在if-then语句中声明test命令,这方便了很多。
注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。
if [ condition ]
then
commands
fi
test命令可以判断三类条件:数值比较、字符串比较、文件比较
(1)数值比较
比 较 | 描 述 |
---|---|
n1 -eq n2 | 检查n1是否与n2相等 |
n1 -ge n2 | 检查n1是否大于或等于n2 |
n1 -gt n2 | 检查n1是否大于n2 |
n1 -le n2 | 检查n1是否小于或等于n2 |
n1 -lt n2 | 检查n1是否小于n2 |
n1 -ne n2 | 检查n1是否不等于n2 |
例如:
#!/bin/bash
# Using numeric test evaluations
value1=10
value2=11
#
if [ $value1 -gt 5 ]
then
echo "The test value $value1 is greater than 5"
fi
#
if [ $value1 -eq $value2 ]
then
echo "The values are equal"
else
echo "The values are different"
fi
别忘了,shell只能处理整数,所以不要比较浮点数!
(2)字符串比较
比 较 | 描 述 |
---|---|
str1 = str2 | 检查str1是否和str2相同 |
str1 != str2 | 检查str1是否和str2不同 |
str1 < str2 | 检查str1是否比str2小 |
str1 > str2 | 检查str1是否比str2大 |
-n str1 | 检查str1的长度是否非0 |
-z str1 | 检查str1的长度是否为0 |
注意,“<”“>”需要前加“\”转义,否则会被当做重定向!
(3)文件比较
比 较 | 描 述 |
---|---|
-d file | 检查file是否存在并是一个目录 |
-e file | 检查file是否存在 |
-f file | 检查file是否存在并是一个文件 |
-r file | 检查file是否存在并可读 |
-s file | 检查file是否存在并非空 |
-w file | 检查file是否存在并可写 |
-x file | 检查file是否存在并可执行 |
-O file | 检查file是否存在并属当前用户所有 |
-G file | 检查file是否存在并且默认组与当前用户相同 |
file1 -nt file2 | 检查file1是否比file2新 |
file1 -ot file2 | 检查file1是否比file2旧 |
五、复合条件测试
if-then语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:
(1) [ condition1 ] && [ condition2 ]
(2)[ condition1 ] || [ condition2 ]
第一种布尔运算使用AND布尔运算符来组合两个条件。要让then部分的命令执行,两个条件都必须满足。
第二种布尔运算使用OR布尔运算符来组合两个条件。如果任意条件为TRUE,then部分的命令就会执行。
六、if-then高级特性
1、双括号
(( expression ))
双括号命令允许你在比较过程中使用高级数学表达式。但shell数学表达是弱项,平日也少用shell计算,所以这段略过。
2、双方括号
[[ expression ]]
双方括号里的expression使用了test命令中采用的标准字符串比较。但它提供了test命令未提供的另一个特性——模式匹配。
在模式匹配中,可以定义一个正则表达式来匹配字符串值。
#!/bin/bash
# using pattern matching
#
if [[ $USER == r* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi
在上面的脚本中,我们使用了双等号(==)。双等号将右边的字符串(r*)视为一个模式,并应用模式匹配规则。双方括号命令$USER环境变量进行匹配,看它是否以字母r开头。如果是的话,比较通过,shell会执行then部分的命令。
七、case命令
你会经常发现自己在尝试计算一个变量的值,在一组可能的值中寻找特定值。
在这种情形下,你不得不写出很长的if-then-else语句,就像下面这样。
#!/bin/bash
# looking for a possible value
#
if [ $USER = "rich" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "barbara" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "testing" ]
then
echo "Special testing account"
elif [ $USER = "jessica" ]
then
echo "Do not forget to logout when you're done"
else
echo "Sorry, you are not allowed here"
fi
学会case命令后,就不需要再写出所有的elif语句来不停地检查同一个变量的值了。
case命令会采用列表格式来检查单个变量的多个值。
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
case命令会将指定的变量与不同模式进行比较。如果变量和模式是匹配的,那么shell会执行为模式指定的命令。可以通过竖线操作符在一行中分隔出多个模式模式。星号会捕获所有与已知模式不匹配的值。
我们可把上述if-the-else语句简化为:
#!/bin/bash
# using the case command
#
case $USER in
rich | barbara)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
参考资料:《linux命令行与shell脚本编程大全.第三版》