shell的流程控制
在Bash Shell中,流程控制有两大类:“选择”和“循环”。属于“选择”的有:if、case;属于“循环”的有:for、while、until;命令select即属于“选择”,也属于“循环”。不管是哪一种,均需进行条件测试,在根据测试结果,决定程序流程进行的方向。
Bash的内置变量$?,用来存储每个命令执行后传回的状态值。每一个命令执行后,都会传回一个结束状态值,如果执行成功,传回0,如果执行失败,则传回非0值。
1:if条件判断
简易的if语法:if-then-else
(1)if-then
if 条件测试;then
命令区域
fi
(2)if-then-else
if 条件测试1;then
命令区域1
elif条件测试2;then
命令区域2
else
命令区域3
fi
2:条件测试的写法
(1)执行某一个命令的结果
包括管道命令: 命令1 | 命令2 | 命令3 ,称为pipeline,其结束状态为最后一个命令执行的结果。
范例:
- #!/bin/bash
- #grep -q "rm" fn.sh是寻找fn.sh文件里是否包含关键词rm。选项-q表示不显示,仅借助$?来传回执行结果。
- if grep -q "rm" fn.sh; then
- echo "find rm command ."
- else
- echo "not find."
- fi
(2)传回某一命令执行结果的相反值
其形式为:!命令
注意:!和命令之间要有空格符隔开。
如果命令传回值为0,则加上!之后传回值为1;反之,如果命令传回值为非0,则加上!之后,传回值为0.
- #!/bin/bash
- if ! grep -q "rm" fn.sh; then
- echo "find rm command ."
- else
- echo "not find."
- fi
(3)使用复合命令:((算式))
如果算式的运算结果不为0,则传回真值(0),否则如果运算结果为0,则传回假值(1)。
(4)使用Bash关键词‘[[’、‘]]’组成的式子:[[判断式]]
判断式会传回真假值,传回0为真,非0为假。注意,[[的后面、]]的前面,都至少要有一个(含)以上的空格符才行。
(5)使用内置命令:test判断式
test是Bash的内置命令,可传回“判断式”的结果,真值传回 0,假值传回1.
(6)使用内置命令:[]
其形式为:[判断式]
[]和test的用法是相同的,两者可更改互换。
(7)使用-a,-o进行逻辑组合:
例如:
[ -r filename1 -a -x filename ]
如果filename1可读且可执行,则为真。-a即“且”之意。
[ -r filename1 -o -x filename ]
如果filename1可读或可执行,则为真。-o即“或”之意。
(8)命令1 && 命令2
&&为为逻辑的AND,其运作方式是:如果“命令1”执行结果为真,才会执行“命令2”;如果两个皆为真,则传回真值0,否则传回假值1 。
凡是“[判断式] && 指令”的形式,就视为一种隐形的if-then语法。
例如:
- # 如果$ps1的变量值不为空,就执行return指令。
- [ -z "$PS1" ] && return
- #如果/proc/net/if_inet6存在,就显示主机的核心支持IPv6协议。
- [ -f /proc/net/if_inet6 ] && echo 'The macshine is support IPv6.'
(9)命令1 || 命令2
||称为逻辑的OR,其运作方式是:如果“命令1”执行结果为假,才会执行“命令2”;如果两个之中有一个为真,则传回真值0,否则传回假值1 。
||的特性也可以当作一种隐形的if语法:例如
- prefix="/home"
- defpath="/usr/local/bin"
- #因为$prefix非空,所以,${prefix:-}变量扩展的传回值为prefix的变量值(非空),[ -z ${prefix:-}]
- #对空值条件测试失败,其结果为假,根据||的特性,会接着执行prefix=${defpath%/*},它会由$defpath后方
- #删去符合样式/*的最短字符串,即删除/bin,因此$prefix的值为/usr/local。
- [ -z ${prefix:-} ] || prefix=${defpath%/*}
(10)&&和||合用
&&和||合用,也可以有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
注意:在条件测试的方法中,[[]]和test、[]的意思和用法是相近的,但[[]]比test和[]更自由一点,因为[[]]不必担心某些Bash特殊字符对运算符的影响,不必写一堆转义字符。
3:case条件判断
case的语法
- case 待测项 in
- 样式串行1) 命令区域1;
- 样式串行1) 命令区域2;
- 样式串行1) 命令区域3;
- ......
- *) 命令区域;;
- esac
说明:
如果“待测项”和某一样式串行对比符合的话,case会选择性的执行该样式后面的命令区域。
命令区域,可以使单一指令或多行指令,但最后一个指令要以;;结束。
样式串行可用字符串和通配符组成,默认会区别大小写,换言之,Jack和jack是不同的样式。
如果样式串行中有好几个字符串要对比,要用|隔开。字符|有“或”的意思,意即:只要符合其之一,就算对比成功。
样式串行可写成'(样式串行)',或者省略左边的括号,可写成‘样式串行)’。一般,以后面这个写法,较受欢迎。
样式*)通常放在case最后一个区域,用来捕捉不符合样式的其他所有情况。
最后esac结束case语法。esac是case的反写。
nocasematch,可以让Base在对比样式时忽略大小写。
例如:shopt -s nocasematch
(1)高级样式
Base的样式,可又一下组件组成,成为样式串行:
如果样式有好几项组件,彼此之间用|隔开,例如 ps | top | w 。对比时,只要符合其中一项,就算对比成功。
另外,如果Bash的选项extglob有打开,例如
shopt -s extglob
那么,Bash还支持几种高级样式:
4:for循环
for循环的运作方式,是将串行的元素一一取出,依序放入制定的变量中,然后重复执行含括的的命令区域(在do和done之间),直到所有元素取尽为止。
其中,串行是一些字符串的组合,彼此用$IFS所定义的分隔符(如空格符)隔开,这些字符串被称为字段。
for的语法结构为:
|
说明:行1,将串行的字段迭代放入变量中。
行2~4,接着重复执行do和done之间的命令区域,直到串行中每一个字段均处理过为止。
例如:查看/var目录下各子目录占用磁盘的情况。
- #!/bin/bash
- DIR="/var"
- cd $DIR
- for f in $(ls $DIR)
- do
- [ -d $f ] && du -s $f
- done
另外一种for的用法:
- for ((初始条件;终止条件;异动项))
- do
- 命令区域
- done
for无穷循环
- for ((;1;1))
- do
- 命令区域
- done
5:while循环
while的语法:
- while 条件测试
- do
- 命令测试
- done
说明:
例如:
- #!/bin/bash
- declare -i i=1
- declare -i sum=0
- while ((i<=10))
- do
- let sum+=i
- let ++i
- done
- echo $sum
使用while循环,读取文件内容
while循环,经典的用法就是搭配转向输入,读取文件的内容。
例如:
- #!/bin/bash
- #filename:readfile.sh
- while read line
- do
- echo $line
- done < cvsfile
- #!/bin/bash
- #filename:readfile2.sh
- IFS=':'
- while read f1 f2 f3 f4 f5 f6 f7
- do
- echo "账号:$f1,login Shell是:$f7"
- done < "/etc/passwd"
- #!/bin/bash
- #filename:readfile3.sh
- IFS=':'
- cat /etc/passwd | {
- while read r1 r2 r3 r4 r5 r6 r7
- do
- echo "账号:$f1,login shell是:$f7"
- done
- }
while无穷循环
- while ((1))
- do
- echo "夜不眠"
- done
把((1))改成[1]也可以。
或者
- while true
- do
- echo '夜不眠'
- done
或者
- while :
- do
- echo '夜不眠'
- done
按下Ctrl+C,可以终止循环。
6.until循环
while循环的测试条件是真值,until循环的测试值是假值。
until的语法:
- until 条件测试
- do
- 命令区域
- done
说明:
行1,如果条件测试结果为假(传回值不为0),就进入循环。
行3,执行命令区域。这些命令中,应该有改变条件测试的命令,这样,才有机会在有限步骤结束执行until的循环(除非你想要执行无穷循环)。
行4,回到行1,执行until命令。
until无穷循环
until无穷循换的写法如下:
- until ((0))
- do
- echo 'Bash forever.'
- done
或者:
- until false
- do
- echo 'Bash forever.'
- done
按下ctrl+c,可以终止循环。
7.select命令
select命令可以建立简易的列表,其语法结构和for一样。
select的语法如下:
- select 选项变量 in 串行
- do
- 命令区域
- done
说明:
- #!/bin/bash
- PS3='请选择:'
- select f in *
- do
- echo "你键入的编号是$REPLY,选择的文件是:$f"
- done
说明:
8:break和continue
break命令
对于前面提到的4种循环for、while、until、select而言,如果想要提早结束循环,可在循环中使用break命令。执行break时,会跳出一层的循环,如果想要跳出多层循环,可在break命令之后加上层数n(n一定要大于或等于1)。
- #!/bin/bash
- for ((i=1;i<=10;i++))
- do
- for ((j=1;j<=10;j++))
- do
- r=$((i+j))
- if [ $r -eq 19 ];then
- break 2
- fi
- echo $r
- done
- done
continue名利
continue一样可以运用在for、while、until、select这4种循环语法中。和break不同的是continue会跳过本次循环,重新由下一个循环开始执行。
- #!/bin/bash
- for ((i=1;i<=10;i++))
- do
- if [ $i -eq 6 ];then
- continue
- fi
- echo $i
- done
如果有多层循环,可以在continue后面指定由哪一层的循环重新开始执行。例如:continue 2,表示从里面往外跳出2层循环,然后由哪一层的循环开始。