流程控制
命令的结束状态
Bash Shell Script可由许多命令组成。每一个命令执行后,都会传回一个结束状态值,如果执行成功,传回0,如果执行失败,则传回非0值。执行失败,也分成许多状态,例如:某个命令执行失败,产生严重的信号n,则其结束状态传回值为128+n。如果执行命令时,发现该命令不存在,则传回值为127。命令存在但没有执行权,则传回值126。
Bash的内置变量$?,用来存储每个命令执行后传回的状态值。如执行noexe.sh时,Bash在搜寻路径中找不到这个Script,结束状态变量$?的值为127。
条件判断
示例:
if grep -q ^ols3 /etc/passwd; then
echo 'ols3这个账号存在'
fi
使用grep命令寻找/etc/passwd文件中是否有ols3这个关键词出现在行首(以^表示),如果有,表示该帐号存在,就显示存在的信息。(grep -q的-q表示grep之后不在输出端输出)
if-then-else语法规则
语法一:
if 条件测试; then
命令区域
fi
示例:if (( 2 < 10 )); then echo '真'; fi
备注:如果if then放在同一行,则then前需要加;,如果命令区域和fi放在同一行,则fi前需要加;
语法二:
if 条件测试; then
命令区域1
else
命令区域2
fi
示例:
if [ -d /root/tmp ]; then
echo '/root/tmp 目录存在.'
else
echo '/root/tmp 目录不存在.'
fi
语法三:
if 条件测试1; then
命令区域1
elif 条件测试2; then
命令区域2
else
命令区域3
fi
示例:
#! /bin/Bash
declare -i a b
a=$1; b=$2
if ((a<b)); then
echo "$a小于$b"
elif ((a>b)); then
echo "$a大于$b"
else
echo "$a等于$b"
fi
备注:在此例中,(())是Bash的复合命令(compound command),内置算式。((算式))可对算式的运算结果传回真假值:如果算式的结果非0,则结束状态传回0,表示该算式主真;如果算式的结果为0,则结束状态传回1,表示该版式为假。它的意义同于 let "算式"
条件测试
if条件判断中,条件测试的结果是真是假,就看其传回的值是否为0。
条件测试的写法,有以下10种:
执行某一个命令的结果
这里的命令,可包括管道命令,例如:命令1|命令2|命令3,称为pipeline,其结束状态为最后一个命令执行的结果。
示例:
#! /bin/Bash
if grep -q "rm" fn.sh; then
echo "find rm command"
else
echo "not find."
fi
在条件测试中,执行的命令是:grep -q "rm" fn.sh,它是寻找fn.sh文件里是否含有关键词rm。选项-q表示不显示,仅借助$?来传回执行结果.
传回某一命令执行结果的相反值
其形式为: ! 命令(注意: !和命令之间要有空格符隔开)
示例:
#! /bin/Bash
if ! grep -q "rm" fn.sh; then
echo "not find."
else
echo "find rm command."
fi
在条件测试中,如果找不到关键词,就传回0(真值),找到了,则传回1(假值)
使用复合命令:((算式))
如果算式的运算结果不为0,则传回真值(0),否则如果运算结果为0,则传回假值(1).
使用Bash关键词 [[ ]]组成的式子: [[ 判断式 ]]
判断式会传回真假值,传回0为真,非0为假.[[的后面,]]的前面,都至少含有一个以上的空格符。
示例:
#!/bin/sh
if [[ str > xyz ]]; then
echo "字符串str比较大"
else
echo "字符串str比较小"
fi
使用内置命令: test判断式
test是Bash的内置命令,可传回“判断式”的结果,真值传回0,假值传回1。
#! /bin/Bash
if test "str" \> "xyz"; then
echo "字符串str比较大"
else
echo "字符串str比较小"
fi
备注:>对Bash而言是特殊字符,要用\转义。
使用内置命令: []
[]和test的用法是相同的,示例如下:
if [ "str" \> "xyz" ]; then
echo "字符串str比较大"
else
echo "字符串str比较小"
fi
使用 -a、-o进行了逻辑组合:
[ -r filename -a -x filename ] //如果filename可读且可执行,则为真。-a为“且”
[ -r filename -o -x filename ] //如果filename可读或可执行,则为真。-o为“或”
命令1 && 命令2
命令1执行结果为真,才会执行命令2,如果两个皆为真,则传回真值0,否则传回假值1。
示例:
#! /bin/Bash
a=20
if grep -q "rm" fn.sh && [ $a -lt 100 ] ; then
echo "ok"
else
echo "not ok"
fi
&&的特性,经常拿来当做是一种隐形的if语法。例如:
[ -z "$PS1" ] && return
这行程序代码的意思是:先判断$PS变量值是否为空,如果是,就执行return命令,由子shell环境返回到父Shell,这等于是结束执行该Script。作用等同于下列语句:
if [ -z "$PS1" ]; then
return
fi
命令1||命令2
||称为逻辑的OR,其动作方式是:如果"命令1"执行结果为假,才会执行"命令2";如果两个之中有一个为真,则传回真值0,否则传回假值1。
||的特性,也可当做是一种隐形的if语法。例如:
prefix="/home"
defpath="/usr/local/bin"
[ -z ${prefix:-} ] || prefix=${defpath%/*}
因$prefix非空,所以,${prefix:-}变量扩展的传回值为prefix的变量值,[ -z ${prefix:-} ]对空值的条件测试失败,其结果为假,根据||的特性,会接着执行prefix=${defpath%/*},它会由$defpath后方删去符合样式/*的最短字符串,即删去/bin,因此,$prefix的值为/usr/local。
等效的if语句如下:
prefix="/home"
defpath="/usr/local/bin"
if [ ! -z ${prefix:-} ]; then
prefix=${defpath%/*}
fi
&&和||合用
&&和||合用,也可以有if-then-else的效果,例如:
[ -n ${DEBUG:-} ] && set -v || set +v
这行程序代码,使用-n测试变量DEBUG是否有设非空值,如果有,表示要进行了排错,接着执行逻辑AND的下一条指令:set -v;如果无,则不进行排错,而改执行逻辑OR的下一个指令:set +v,它会把显示程序代码的功能关闭.
if [ -n "$DEBUG" ]; then
set -v
else
set +v
fi
往后,凡是"[ 判断式 ]&&指令1||指令2"的形式,就被视为一种隐形的if-then-else的语法。
小结:
在上述这些条件测试的方法中,[[]]和test、[]的意思和用法是相近的,但[[]]比test和[]更自由一点,因为[[]]不必担心某些Bash特殊字符对运算符的影响,不必定一堆字符的怪符号,如[[ str < xyz ]]是正确的语法,但在[]中却要写成[ str \< xyz ],这种陷阱很容易忘记。在[[]]中,<、>、&&、||等都可以自由地使用,不必使用转义字符。
除了[[]]之外,在Bash中,(())也不必理会上述提到的特殊字符的影响。在[[ 判断式 ]]中,如果使用==或!=,且在这两个运算符右方的字符串没有加上单引号或双引号,则==和!=会视为想要对比该字符串所形成的"样式",如果相符,传回0,如果不符,传回1。
示例1:
#! /bin/Bash
a="str"
if [[ $a == ??? ]]; then
echo "Match." //用$a的值对比样式???
fi
if[[ $a == "???" ]]; then //判断$a和字符串???是否相等.
示例2:
#! /bin/Bash
str='abc 123 987 what u want.'
if [[ "$str" == *[.?\!] ]]; then
echo "变量str是一字符串,则最后一个字符是 '.'、'?'或'!'其中一个字符."
fi
\!是使用\转义!的特殊含意(!原是调用某一历史指令的意思)
自Bash3.2版开始,支持在[[]]中运用新的对比运算符=~,用法和perl的正则表达式差不多,请参考13.1节正则表达式中的说明。
条件判断式的真假值
关于文件属性的条件判断式,详见<<实战linux_shell编程与服务器管理>>P195,对应PDF P211页.
示例:
#! /bin/Bash
[ -e "/etc/hosts" ] || (echo '/etc/hosts文件不存在'; exit 1)
if [ "$?" -eq 1 ]; then
exit
fi
echo '/etc/hosts文件存在,后续处理继续执行下去....'
备注:使用-e判断/etc/hosts这个文件是否存在,如果不存在,则()开启一个子shell,显示文件不存在的信息,然后传回离去状态值1。如果离去状态为1,就结束script.
关于字符串的条件判断式,详见<<linux_shell编程与服务器管理>>P197,对应PDF P213页。
示例1:
#! /bin/Bash
if [ "$LOGNAME" != "root" ]; then
echo '本程序须使用root权限执行.'
exit 1
fi
echo '现在你正以root权限执行本程序...'
在对比字符串时,最好把变量名称用双引呈含括,以免变量内容为空时造成语法错误。
为了提高移植性,让script可在其他shell环境中执行,如传统的Bourne shell会利用以下技术来判断字符串是否相等,以避免空值变量造成语法错误:
#! /bin/Bash
NAME=$1
if[X"$NAME" = X"Joy" ]; then
echo '你是Joy.'
else
echo '你不是Joy.'
fi
这样可以保证语法完整。这里的X,可换成其他任一英文字符。
关于算式的条件判断式,详见<<linux_shell编程与服务器管理>>P200,对应PDF P216页。
关于Bash选项的条件判断式,详见<<linux_shell编程与服务器管理>>P200,对应PDF P216页。
#! /bin/Bash
set -o
if [ -o history ]; then
echo 'Bash选项history开启.'
else
echo 'Bash选项history关闭.'
fi
case条件判断
语法规则:
case 待测项 in
样式串行1) 命令区域1;;
样式串行2) 命令区域2;;
样式串行3) 命令区域3;;
*) 命令区域;;
esac
示例:
#!/bin/sh
shopt -s nocasematch //让Bash在对比样式时忽略大小写
read yname
case $yname in
Jack | John | Joe)
echo 'Well...'
echo "Long time no see"
echo "How do you do ?" ;;
(Mary|May) //()和)是一样的
echo 'Nice to meet you.';;
C*) echo 'Long time no see.';;
*) echo 'Hi!';;
esac
高级样式
case语法中,是否执行某一个命令区域,需要待测项目是否符合“样式”。Bash的样式,可由以下组件组成,称为样式串行:
1) 字符串: 如Jack
2) 通配符: 比如*表示任意长度的字符串,也包括空字符串;C*表示以C字符开头的字符串;????表示4个字符的字符串
3) 字符集合: 如[p-r]im,表示pim、qim、rim。
4) 项目分隔符 |
另外,如果Bash的选项extglob有打开,例如 shopt -s extglob,那么Bash还支持几种高级的样式对比,详见文档
? * + @ !