Shell(八):循环与结构化命令
反复执行某一个命令或某一组命令,需要用到循环结构化命令。
循环命令用于特定条件下决定某些语句重复执行的控制方式,具有封闭型的单入单出性质。即进入循环结构后,只要循环条件未达到结束状态,就时钟执行循环体内的操作。
Shell中提供三种常用的循环语句,分别是 for 循环、while循环和unit循环,并提供了循环控制符 break 和 continue 。
1、for循环
1.1、列表for循环
列表for循环语句用于将一组命令执行已知的次数,基本格式:
for variable in {list}
do
...
done
do 和 done之间的命令称为循环体,执行次数和list列表中常数或字符串的个数相同。
当执行for循环时,首先将 in 后 list 列表的第一个常数或字符串赋值给循环变量,然后执行循环体;接着将list第二个常数或字符串赋值给循环变量,再次执行循环体。该过程将一直持续到list列表中无其他的常数或字符串,然后执行done命令后的命令序列。
新建 for01.sh 脚本,详情如下:
#!/bin/bash
# for循环
for var in 1 2 3 4 5
do
echo "$var times."
done
执行结果如下:
Linux Shell中支持for循环中使用略写的计数方式,新建for02.sh脚本,详情如下:
#!/bin/bash
# for 循环略写的计数方式
for var in {1..5}
do
echo "$var times."
done
执行结果如下:
Linux Shell中还支持按规定的步数进行跳跃的方式实现列表for循环,新建 for03.sh 详情如下:
#!/bin/bash
# 计算 0 ~ 100内所有偶数之和
sum=0
for i in {0..100..2}
do
let "sum+=i"
done
echo "sum=$sum"
首先给sum赋值,通过i的按步数2不断递增,最终计算出sum的值,脚本的执行结果如下:
也可以使用 seq 命令来实现按步数进行跳跃操作,新建for04.sh 脚本,详情如下:
#!/bin/bash
# 计算 0 ~ 100内所有偶数之和
sum=0
for i in $( seq 0 2 100 )
do
let "sum+=i"
done
echo "sum=$sum"
seq命令是Linux预设的外部命令,一般用做一堆数字的简化写法,( seq 0 2 100 ) 有三个常数,其中 0 表示起始数,2表示跳跃步数,100表示结束条件值。脚本执行结果如下:
for循环也可对字符串进行操作,新建 for05.sh 详情如下:
#!/bin/bash
for holiday in 春节 清明节 端午节 中秋节 国庆节
do
echo "$holiday"
done
在list列表中列出了节假日,然后通过列表for循环将其显示给用户,脚本执行结果如下所示:
可以通过for循环显示当前目录下所有的文件,新建for06.sh,脚本详情如下:
#!/bin/bash
# 通过ls命令和列表for循环显示当前目录下的文件
for file in $( ls )
do
echo "file: $file"
done
在for06.sh中,通过命令ls显示当前目录下的所有文件,然后通过不断的循环赋值给file,将其对应的文件名显示出来,脚本执行结果如下:
列表for循环可以实现通过命令行来传递脚本中for循环列表参数,新建 for07.sh,脚本详情如下:
#!/bin/bash
# 列表for循环实现通过命令行来传递脚本中的for循环列表参数
# 提示用户输入参数个数
echo "number of args is $#"
# 提示用户输入内容
echo "Input Content : "
# 通过命令行来传递脚本for循环列表参数
for arg in "$*"
do
echo "$arg"
done
先显示用户输入参数的格式,然后输出用户输入的命令行参数内容,执行结果如下:
参数列表可以是数字,也可以是字符串,但输入是以空格进行分隔的,若存在空格,脚本执行时会认为存在另一参数。
1.2、不带列表for循环
不带列表for循环执行时,由用户指定参数和参数的个数。基本格式如下:
for variable
do
...
done
do和done之间的命令称为循环体,Shell会自动将命令行键入的所有参数依次组织成列表,每次将一个命令行键入的参数显示给用户,直至所有的命令行中的参数都显示给用户。这种结构的for循环和带列表的for循环功能结构完全一致。
for varibale in "$@"
do
...
done
不带列表的for循环同样通过命令行来传递参数列表,脚本 for08.sh 详情如下:
#!/bin/bash
# 不带参数列表 for 循环
# 提示用户输入参数个数
echo "number of args is $#"
# 提示用户输入内容
echo "Input Content : "
# 通过命令行来传递脚本for循环列表参数
for arg
do
echo "$arg"
done
执行结果如下:
不带列表for结构比for循环结构简洁,但只可从命令行来传递参数,这种方式在Shell编程中使用相对较少,只限于命令行传递参数。
1.3、类 C 风格的for循环
类C风格的for循环也可被称为计次循环,一般用于循环次数已知的情况。语法格式如下:
for (( expr1:expr2:expr3 ))
do
...
done
表达式 expr1 为循环变量赋初值的语句;表达式 expr2 决定是否进行循环的表达式,当判断 expr2 退出状态为 0 时,执行 do 和 done 之间的循环体,当退出状态为非0时,将退出for循环执行done后的命令;表达式 expr3 用于改变循环变量的语句。
类C风格的for循环结构中,循环体也是一个块语句,要么是单条命令,要么是多条命令,但必须在do和done之间。
新建 for09.sh 脚本,详情如下:
#!/bin/bash # 类C风格的for循环实现输出前5个正整数 for(( i = 1; i <= 5; i++ )) do echo "$i times." done
for循环中声明了变量 i,并赋值为1,因为 i 用于控制循环执行的次数和结束条件,所以 i 称为循环变量,接着判断 i 是否小于或等于 5,若 i 小于或等于 5 成立,则执行循环体 do 和 done 之间的命令,而后执行修正表达式 'i++',将i的值加1,再次判断 i 小于或等于 5 是否成立,直至循环结束。执行结果如下:
类C风格的for循环注意事项:
·如果玄幻条件最初的退出状态为非0,则不会执行循环体;
·当执行更新语句时,若循环条件的退出状态永远为0,则for循环将永远执行下去,从而产生所谓的死循环;
·Linux Shell中不运行使用非整数类型的数作为循环变量;
·若循环体中的循环条件被忽略,则默认的退出状态为0。
2、while循环
while循环语句也称前测试循环语句,它的循环重复执行次数是利用一个条件来控制是否继续重复执行这个语句。while循环格式如下:
while expression
do
...
done
while循环语句命名为前测试循环,因为需要先判断此循环的条件是否成立,然后才做重复执行的操作。即,while循环语句执行的过程是:先判断expression的退出状态,如果退出状态为0,则执行循环体,并且在执行完循环体后,进行下一次循环,否则退出循环执行done后的命令。
为避免死循环,必须保证在循环体中包含循环出口条件,即存在expresiion的退出状态为非0的情况。while循环可以分为四种情形,
2.1、计数器控制的while循环
这种情形是已经准确知道要输入的数据或字符串的数目,在这种情况下可采用计数器控制的while循环结构来处理。由于指定了循环次数N,可初始化计数器值,通过计数器值与N进行比较,若小于等于N,则执行循环体,该循环体将反复执行,直至计数器中的值大于N为止。格式如下:
counter=1
while expression
do
...
let command to operate counter
...
done
计数器控制的while循环计算1~100内所有奇数之和,详情如下:
#!/bin/bash # 计算 1 ~ 100 内所有的奇数之和 # 对sum赋初值 sum=0 # 对计数器i赋初值 i=1 # 使用计数器控制的while循环 while ((i <= 100)) do let "sum+=i" let "i += 2" done # 输出sum echo "sum=$sum"
初始化sum值为0,然后初始化计数器i值为1,接着通过不断测试循环条件i是否小于等于100,若小于等于100,将执行循环体,在循环条件中设置了计数器加2的操作。 脚本执行结果如下:
2.2、结束标记控制的while循环
设置一个特殊的数据值来结束while循环,该特殊数据值称为结束标记,其通过提示用户输入特殊字符或数字来操作。当用户输入该标记后结束while循环,执行done后的命令。while循环的形式如下:
read variable
while [[ "$variable" != end ]]
do
read variable
done
结束标记控制的while循环用法,新建脚本 while02.sh,脚本内容如下所示:
#!/bin/bash
# 演示使用结束标记控制的while循环实现猜1~10内的数
# 提示用户输入 1~10 内的整数
echo "Please input the num(1-10)"
read num
# while 循环实现猜数游戏
while [[ "$num" != 4 ]]
do
if [ "$num" -lt 4 ]
then
echo "值太小,再试一次!"
read num
elif [ "$num" -gt 4 ]
then
echo "值太大,再试一次!"
read num
else
exit 0
fi
done
执行结果如下:
2.3、标志控制的while循环
标志控制的while循环使用用户输入的标志值来控制循环的结束,避免了用户不知道循环结束标记的麻烦。在该情形下,while循环的形式如下所示:
flag=1
while (( flag != 0 ))
do
...
if expression
then
flag=0
fi
...
done
使用标志控制的while循环中的signal和variable可以是数字,也可以是字符串。
#!/bin/bash
# 标志控制的while循环
echo "请输入数字(1~10):"
read num
# 初始化标志的值
signal=0
while [[ "$signal" != 1 ]]
do
if [[ "$num" -lt 4 ]]
then
echo "值太小,请重试"
read num
elif [[ "$num" -gt 4 ]]
then
echo "值太大,请重试"
read num
else
signal=1
echo "恭喜你,猜对了"
fi
done
通过while04.sh脚本可以看出,使用标志控制的while时,当用户输入4时在循环体内执行一次,但使用结束标记控制的while循环在用户输入4时无法在循环体内执行其语句,因为结束标记就是用户输入的结果为4时,退出循环。执行结果如下:
2.4、命令行控制的while循环
使用命令行来指定输出参数和参数个数,该形式下,while循环通常与shift结合使用,其中shift命令使变量下移一位(即$2代替$1、$3代替$2),并且使$#变量递减,当最后一个参数也显示给用户后,$#就会等于0,同时 $* 也等于空。
while [[ "$*" != "" ]]
do
echo "$1"
shift
done
新建while05.sh脚本,详情如下:
#!/bin/bash
# 提示输入用户个数
echo "number of args is $#"
# 提示用户输入内容
echo "what your input is:"
while [[ "$*" != "" ]]
do
echo "$1"
shift
done
由命令行输入字符串和数字,并将结果显示给用户,若用户不输入任何参数,就就根本不会执行echo和shift命令,因为$#为0。执行结果如下:
3、until循环
在执行while循环时,只要expression的退出状态为0,将一直执行循环体。until命令和while命令类似,区别是until循环中expression的退出状态不为0时,循环体将一直执行下去,直到退出状态为0,until循环的结构如下:
until expression
do
...
done
首次测试expression的退出状态为0,一次也不执行循环体。
新建until01.sh脚本,,该脚本计算1~9的平方,详情如下:
#!/bin/bash
# 初始化循环变量
i=1
# 使用until循环计算1~9的平方
until [[ "$i" -gt 9 ]]
do
let "square=i*i"
echo "$i * $i = $square"
let "i++"
done
通过let命令将每次i的平方保存到变量square中,然后通过echo命令输出,其控制命令为判断i是否大于9,若i不大于9,将执行循环体,若大于9,将结束循环。执行结果如下:
while循环与until循环类似,区别在于:while循环在条件为真时继续执行循环,而until则在条件为假时执行循环。
4、嵌套循环
一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。在外部循环的每次执行过程中都会触发内部循环,直至内部完成一次循环,才接着执行下一次的外部循环。
新建nest01.sh脚本,详情如下:
#!/bin/bash # 使用for循环嵌套实现九九乘法表 for (( i = 1; i <= 9; i++ )) do # 内层循环 for(( j = 1; j <= i; j++ )) do let "temp=j*i" echo -n "$i*$j=$temp " done echo "" done
脚本nest01.sh在内循环中使用了temp暂时存储i*j的结果,然后通过echo命令加-n,表示不换行输出。命令 echo "" 实现换行操作。
执行结果如下:
5、循环控制符
在Shell编程中,有时会需要从循环中退出,若退出循环,可使用break循环控制符;若是退出本次循环执行后继续循环,则可使用continue循环控制符。
5.1、break循环控制符
break语句可应用在for、while、until循环语句中,用于强行退出U型你换,忽略循环体中任何其他语句和循环条件的限制。
新建break01.sh脚本,详情如下:
#!/bin/bash sum=0 for (( i=1; i <= 100; i++ )) do let "sum+=i" done echo "1+2+...+100=$sum" total=0 for (( j=1; j <= 100; j++ )) do let "total+=j" if [ "$total" -gt 1200 ] then echo "1+2+...+$j=$total" break fi done
for循环设计为计算从1至100内所有整数的和,当累加结果大于1200时,使用break循环控制符终止了for循环语句,所以当循环结束时,i的值并不等于100,而是等于49。执行结果如下:
需注意的是,break语句仅能退出的当前的循环,若是两层循环嵌套,且break循环控制符在内层循环中,则break语句仅能退出当前的循环,如果想要跳出整个循环,则需要在外层循环中使用break循环控制符。
新建break02.sh脚本,详情如下:
#!/bin/bash for (( i=1; i<=9; i++ )) do for(( j=1; j<=i; j++ )) do let "temp=i*j" if [ "$temp" -eq 7 ] then break fi echo -n "$i*$j=$temp " done echo "" done echo "-------------------------------------------------------------" for (( m=1; m<=9; m++ )) do if [ "$m" -eq 7 ] then break fi for (( n=1; n<=m; n++ )) do let "result=m*n" echo -n "$m*$n=$result " done echo "" done
执行结果如下:
5.2、continue循环控制符
continue循环控制符应用在for、while和until语句中,用于让脚本跳过其后面的语句,执行下一次循环。
新建continue.sh脚本,详情如下:
#!/bin/bash # 显示200以内能被7整除的数 # 初始化中间变量 m=1 for (( i=1; i<200; i++)) do # 判断是否能被7整除 let "temp=i%7" if [ "$temp" -ne 0 ] then continue fi echo -n "$i " # 换行 let "temp2=m%7" if [ "$temp2" -eq 0 ] then echo "" fi let m++ done
当i不能被7整除时,将跳过输出命令,直接进入下一次循环,若i能被17整除,将执行输出命令,当计数变量m取余结果为7时执行换行命令。执行结果如下:
6、select结构
select结构不对可执行结果的代码块进行循环操作,但与循环结构类似,一开在代码块的顶部或底部的条件判断决定1程序的分支。
Select是bash的拓展结构,用于交互式菜单显示,用户可以从一组不同的值中进行选择,基本结构如下:
select variable in {list}
do
....
done
新建select.sh脚本,详情如下:
#!/bin/bash
echo "What is your favorite color?"
select color in "red" "blue" "green" "white" "black"
do
break
done
echo "You have selected $color"
执行结果如下:
select还有一种不带参数列表的select结构,该结构通过命令行来传递参数列表,由用户自己设定参数列表,格式为:
select variable
do
...
done
新建select02.sh脚本,详情如下:
#!/bin/bash
echo "What is your favorite color?"
select color
do
break
done
echo "You have selected $color"
通过命令行传递列表参数,罗列出命令行参数,最后通过用户回答显示最终结果,执行结果如下: