在Shell编程中,很多时候需要反复执行一条或一组命令,比如说连续打印10条“Hello World”—虽然说不借助于循环而是写10条echo "Hello World"也可以完成同样的工作,但如果是打印100条呢?这时候如果还是采用这种写法,可能会让你烦不胜烦,这时就不得不借助于循环了。Shell中的循环主要有for、while、until、select几种。
for循环是Shell中最常见的循环结构,根据书写习惯又分为列表for循环、不带列表的for循环以及类C的for循环。
带列表的for循环用于执行一定次数的循环(循环次数等于列表元素个数),其语法结构如下:
for VARIABLE in (list)
do
command
done
列表的形式多种,如果列表是数字,如果只是少数的几个数字还是比较方便一一枚举出来的,但是如果是1到100,这么写就不实际了,Shell提供了用于计数的方式,例如 :
[root@localhost ~]# cat list_number1.sh
#!/bin/bash
for VAR in {1..5}
do
echo "Loop $VAR times"
done
# 或者
[root@localhost ~]# cat list_number2.sh
#!/bin/bash
sum=0
for VAR in `seq 1 100`
#for VAR in $(seq 1 100)
do
let "sum+=VAR"
done
echo "Total: $sum"
#运行结果
[root@localhost ~]# bash list_number2.sh
Total: 5050
不带列表的for循环的结构如下所示:
for VARIABLE
do
command
done
使用不带列表的for循环时,需要在运行脚本时通过参数的方式给for循环传递变量值。
[root@localhost ~]# cat for_list1.sh
#!/bin/bash
for VARIABLE in $@
do
echo-n $VARIABLE
done
#运行时传入参数
[root@localhost ~]# bash for_list1.sh 1 2 3
1 2 3
Shell支持类C的for循环。了解C语言或类C语言的读者一定会对(i=1; i<=10; i++)这样的结构十分熟悉,在Shell中其语法结构如下:
for ((expression1; expression2; expression3))
do
command
done
和for循环一样,while循环也是一种运行前测试语句,相比for循环来说,其语法更为简单, 语法结构如下:
while expression
do
command
done
首先while将测试expression的返回值,如果返回值为真则执行循环体,返回值为假将不执行循环。循环完成后进入下一次循环之前将再次测试。
下面的示例使用类while循环,同时计算1到100的和以及1到100的奇数和。
[root@localhost ~]# cat while_sum.sh
#!/bin/bash
#sum01 用于计算1到100的和
#sum02 用于计算1到100的奇数和
sum01=0
sum02=0
i=1
j=1
while [[ "$i" -le "100" ]]
do
let "sum01+=i"
let "j=i%2" #变量j用来确定变量i的奇偶性,如是奇数则余为1
if [[ $j -ne 0 ]]; then
let "sum02+=i"
fi
let "i+=1"
done
echo "sum01=$sum01"
echo "sum02=$sum02"
#运行结果
[root@localhost ~]# bash while_sum.sh
sum01=5050
sum02=2500
按行读取文件是while一个非常经典的用法,常用于处理格式化数据。比如说下面的一个用于记录学生信息的文件(读者自行创建,内容如下)。
[root@localhost ~]# cat student_info.txt
John 30 Boy
Sue 28 Girl
Wang 25 Boy
Xu 23 Girl
仔细观察这个文件的内容不难发现,第一列是姓名,第二列是年龄,第三列是性别。利用while可按行读取的特性,依次打印学生信息。
[root@localhost ~]# cat while_file.sh
#!/bin/bash
while read LINE
do
NAME=`echo $LINE | awk '{print $1}'`
AGE=`echo $LINE | awk '{print $2}'`
Sex=`echo $LINE | awk '{print $3}'`
echo "My name is $NAME,I'm $AGE years old,I'm a $Sex"
done < student_info.txt
#运行结果
[root@localhost ~]# bash while_file.sh
My name is John,I'm 30 years old,I'm a Boy
My name is Sue,I'm 28 years old,I'm a Girl
My name is Wang,I'm 25 years old,I'm a Boy
My name is Xu,I'm 23 years old,I'm a Girl
until循环也是运行前测试,但是until采用的是测试假值的方式,当测试结果为假时才继续执行循环体,直到测试为真时才停止循环。其语法如下:
until expression
do
command
done
下面的示例使用until同时计算1到100的和以及1到100的奇数和。
[root@localhost ~]# cat until_sum.sh
#!/bin/bash
sum01=0
sum02=0
i=1
until [[ $i-gt 100 ]]
do
let "sum01+=i"
let "j=i%2"
if [[ $j-ne 0 ]]; then
let "sum02+=i"
fi
let "i+=1"
done
echo $sum01
echo $sum02
#运行结果
[root@localhost ~]# bash until01.sh
5050
2500
select是一种菜单扩展循环方式,其语法和带列表的for循环非常类似,基本结构如下:
select MENU in (list)
do
command
done
当程序运行到select语句时,会自动将列表中的所有元素生成为可用1、2、3等数选择的列表,并等待用户输入。用户输入并回车后,select可判断输入并执行后续命令。如果用户在等待输入的光标后直接按回车键,select将不会退出而是再次生成列表等待输入。
下面的例子使用select确认用户的输入并交由case处理,之后将根据不同输入执行不同代码段。代码中使用了“|”符,表示选择Saturday和Sunday的效果是一致的。
[root@localhost ~]# cat select_week.sh
#!/bin/bash
select DAY in Mon Tue Wed Thu Fri Sat Sun
do
case $DAY in
Mon) echo "Today is Monday";;
Tue) echo "Today is Tuesday";;
Wed) echo "Today is Wednesday";;
Thu) echo "Today is Thursday";;
Fri) echo "Today is Friday";;
Sat|Sun) echo "You can have a rest today";;
*) echo "Unknown input,exit now" && break;;
esac
done
#运行结果
[root@localhost ~]# bash select_week.sh
1) Mon
2) Tue
3) Wed
4) Thu
5) Fri
6) Sat
7) Sun
#? 1
Today is Monday
#? 6
You can have a rest today
#? 7
You can have a rest today
#? 8
Unknown input,exit now
所谓嵌套循环指的是一个循环语句中的循环体是另外一个循环。前面讲到的for、while、until、select循环语句都可以使用嵌套循环。在嵌套循环中可以多层嵌套,但是要注意,过度的嵌套会让程序变得晦涩难懂,所以除了确实必要的情况下,不建议使用多层嵌套(三层以上的嵌套)
下面使用for的嵌套循环打印九九乘法表,这个经典的程序。
$ cat nesting1.sh
#!/bin/bash
for ((i=1; i<=9; i++))
do
for ((j=1; j<=i; j++))
do
let "mult=$i*$j"
echo -n "$j*$i=$mult "
done
echo
done
$ bash nesting1.sh
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
break用于终止当前整个循环体。一般情况下,break都是和if判断语句一起使用的,当if条件满足时使用break终止循环。
我们可以使用break语句改写一下上面嵌套循环的程序(九九乘法表):
$ cat nesting2.sh
#!/bin/bash
for ((i=1; i<=9; i++))
do
for ((j=1; j<=9; j++))
do
if [[ $j -le $i ]]; then # j小于i输出
let "mult=$i*$j"
echo -n "$j*$i=$mult "
else
break # j如果大于i就停止
fi
done
echo
done
$ bash nesting2.sh
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
continue语句用于结束当前循环转而进入下一次循环,与break不同的是continue并不会终止当前的整个循环体,它只是提前结束本次循环,而循环体还将继续执行;而break则会结束整个循环体。
我们以打印1到100之间的所有素数为例:
$ cat continue.sh
#!/bin/bash
for ((i=1; i<=100; i++))
do
for ((j=2; j<(i/2)+1; j++))
do
if !(($i%$j)); then
continue 2
# 2代表调成两层循环
fi
done
echo -n "$i "
done
echo
$ bash continue.sh
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97