首页 > 代码库 > Bash脚本之循环

Bash脚本之循环

在编写程序的时候有可能需要将一段代码重复的执行0次、1次或多次,在这种时候仅仅只用顺序执行结构就显得有些捉襟见肘了,所以需要一个好的循环结构,而一个好的循环结构必须要包括两个最重要的环节,第一个是进入循环的条件,当满足这个条件的时候就开始循环;第二个是退出循环的条件,也就是当满足这个条件的时候结束循环。来进行循环的命令大概有以下四个:

for:遍历指定的列表;

while:根据逻辑判断的结果;

until:根据逻辑判断的结果;

select:死循环,利用循环机制提供选择列表;

下面分别对这些命令进行介绍:


第一个介绍for命令,使用这个命令可以进行遍历和循环,遍历的意思是给出一个类似数组的东西,然后分别读取数组中的每一个元素,格式为:

for VAR_NAME in LIST ; do 循环体; done

或写成(与if相同,当do和for等命令在同一行的时候需要用分号“;”隔开,当不在同一行的时候就不需要了):

for VAR_NAME in LIST ; do
循环体
done

VAR_NAME为任意指定的变量名称,依次从LIST中取值并赋值给VAR_NAME,循环体一般来说是能够用到变量VAR_NAME的命令或命令的组合,循环的次数就为LIST中元素的个数,LIST的生成方式大致有以下六种:

1) 直接给出

比如:

创建一个脚本,然后将以下内容写入:

#!/bin/bash
for i in a b c
do
echo "$i"
done


[root@localhost class]# bash forbl 
a
b
c

2) 纯整数列表

使用命令seq:输出一个整数列表

seq [FIRST [INCREMENT]] LAST
#!/bin/bash
for i in $(seq 4)
do
echo $i
done
[root@localhost class]# bash forseq 
1
2
3
4

3) 花括号展开

{FIRST..LAST}
#!/bin/bash
for i in {1..5}
do
echo $i
done
[root@localhost class]# bash forzhankai
1
2
3
4
5


4) 命令的执行结果的返回值

#!/bin/bash
for i in $(ls)
do
echo $i
done
[root@localhost class]# ls
forbl  forres  forseq  forzhankai
[root@localhost class]# bash forres 
forbl
forres
forseq
forzhankai


5) GLOBBING来对当前目录下的文件进行名称匹配

#!/bin/bash
for i in f*
do
echo $i
done
[root@localhost class]# ls
forbl  forglob  forres  forseq  forzhankai
[root@localhost class]# bash forglob 
forbl
forglob
forres
forseq
forzhankai

6) 某些变量的引用:$@, $*,这两个符号在这里基本没有区别

#!/bin/bash
for i in $*
do
echo $i
done
[root@localhost class]# bash foryy a b c
a
b
c


注意:这个命令的这种使用方法是将LIST以空格和回车进行分开,也就是说如果一个元素中包含空格,那么这个命令会将其当作两个元素,例如:

#!/bin/bash
for i in a b c
do
echo "$i"
done
[root@localhost class]# bash forbl 
a
b
c
#!/bin/bash
for i in "a b" c
do
echo "$i"
done


[root@localhost class]# bash forbl 
a b
c


for进入循环的条件为LIST中有元素可以取用,那么相对应的退出循环的条件为LIST中的所有元素都被取空,再无元素可用。 for循环有着自己独特的特点,那就是几乎不会出现死循环,但是在执行循环的过程中,这个命令会将LIST整个载入内存,如果LIST中的元素十分巨大,就会过多的消耗内存和CPU资源。


在使用for循环时可以配上一些技巧,例如嵌套使用,比如:

打印九九乘法表

#!/bin/bash
#
for I in {1..9} ; do
  for J in $(seq $I) ; do
    echo -ne "$I*$J=$[I*J]\t"
  done
  echo
done
[root@localhost class]# bash for99
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6  3*3=9
4*1=4 4*2=8  4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81

除了使用for循环来进行遍历,还可以使用控制变量来达到像C语言那样的循环结构,格式为:

for (( 表达式1; 表达式2; 表达式3 )); do 命令; done

或写成:

for (( 表达式1; 表达式2; 表达式3 )) ; do
循环体
done


在上面的格式中

表达式1为变量赋初始值;

表达式2为循环的退出条件;

表达式3为变量值的变化规律;


比如:

#!/bin/bash
for (( I=1; I<=100; I++ )); do
let SUM+=$I
done
echo $SUM
[root@localhost class]# bash forC
5050


在说完for命令之后,说一说while和until命令,while的格式如下:

while 命令; do 命令; done

或写成:

while CONDITION ; do
循环体
done


进入循环条件:CONDITION为真;

退出循环条件:CONDITION为假;


until的使用格式如下:

until 命令; do 命令; done

或写成:

until CONDITION ; do
循环体
done


进入循环条件:CONDITION为假;

退出循环条件:CONDITION为真;


这两个命令十分相近,while是根据条件来执行循环体中的内容,而until是反过来,根据条件的取反来执行循环体中的内容,就是:

while CONDITION ; do CMD ; done

相当于 

until ! CONDITION ; do CMD ; done


注意:对于while和until两个循环结构来讲,如果要实施变量增量操作,必须手动给出,也就是说如果使用一个变量实现自增则需要在循环体内加上一句命令来使变量自增,比如:

利用while和until循环结构,计算100以内所有整数的和;

#!/bin/bash
I=1
while [ $I -le 100 ] ; do
let SUM+=$I
let I++
done
echo $SUM
#!/bin/bash
I=1
until [ $I -gt 100 ] ; do
let SUM+=$I
let I++
done
echo $SUM


在循环体中,有的时候想让循环终止,就需要continue和break这两个命令,首先来看一下continue

continue [n]

提前结束第n层的本次循环,默认n为1,直接进入下一轮条件判断,若符合循环进入条件,则开启下一轮循环,例如:

#!/bin/bash
i=1
while [ $i -lt 3 ]
do
  echo "$i"
  let i++
  continue
  echo "b"
done
[root@localhost class]# bash whilecontinue
1
2

由上面这个例子可以看到在执行循环体中的内容的时候continue命令将“echo "b"”这条命令跳过了,继续去执行下一个循环。这个例子是最基础的使用方法,在日常的使用中我们可以加上一些判断条件,比如说将这个continue命令放到if语句中,如果if一个条件成立,则不执行接下来的命令,举例如下:

#!/bin/bash
i=0
while [ $i -lt 4 ]
do
  let i++
  if [ $i -eq 3 ] ; then
  continue
  fi
  echo "$i"
done
[root@localhost class]# bash whilecontinue1
1
2
4

由上面这个例子可见,在输出“$i”的时候跳过了“3”这个数字。然后再来看一下break这个命令,这个命令和continue用法相同,但是它会直接跳出循环,也就是说使用这个命令之后循环就结束了,格式为:

break [n]

提前技术第n层循环,默认n为1,不再继续后续循环,举例如下:

#!/bin/bash
i=0
while [ $i -lt 4 ]
do
let i++
if [ $i -eq 2 ] ; then
break
fi
echo "$i"
done
[root@localhost class]# bash whilebreak
1
2

在这个脚本中我将上面的那个例子中的continue替换为break,然后将条件改为等于2,由执行后的结果可见原本应该输出“1 2 3”的,现在仅仅输出了“1”,这是因为当“$i”等于3的时候使用了break命令退出了此循环,所以接下来的命令都不会再执行了,甚至连使用continue时输出的“2”也不再输出。


当在写脚本的时候可能需要使用无限循环,那么可以使用如下的两种写法:

while true ; do
循环体
done

或者

until false ; do
循环体
done


这两种写法只是在原来的循环中改变了循环的条件,使条件一直满足来达到无限循环的目的,在这里就不进行举例说明了。在此类的循环结构中,必须适当的使用continue和break,以保证循环不会一直持续下去,否则循环没有终止条件,就会不停地执行,直到用户使用强行停止的命令(强行停止一个命令的快捷键ctrl+c)。


使用while和until这两个命令也能够实现像for命令那样的遍历功能,使用方法如下:

while read LINES ; do
循环体
done < /PATH/FROM/SOMEFILE

或者

until ! read LINES ; do
循环体
done < /PATH/FROM/SOMEFILE

LINES为一个任意的变量名,可以在循环体中进行调用

/PATH/FROM/SOMEFILE为一个能够被当前用户读取的文件,在这里也可以使用输入重定向来达到目的,比如:

#!/bin/bash
while read LINES
do
echo $LINES
done<<EOF
a
a b
a b c
EOF
[root@localhost class]# bash whileread
a
a b
a b c

这种遍历的方法区别于for遍历在于它可以忽略空格带来的误差,可以一行一行的进行读取。


在说完了while和until之后,还有一个循环的命令select,这个命令创建的循环主要用于创建一个菜单式列表,供用户进行选择,列表是按照数字顺序排列的,我们只要选择数字即可。一般来讲,select与case一起使用,select是一个无限循环结构,因此,必须在循环体中使用break命令以退出循环,或者可以使用exit命令直接终止脚本运行。select命令的使用格式如下:

select NAME [in 词语 ... ;] do 命令; done

或者

select NAME [in LIST] ; do
命令
done

比如:

#!/bin/bash
select c in qwe asd zxc
do
:    #在这里的“:”是代表继续执行的意思,如果没有这个命令会报错
done
[root@localhost class]# bash select
1) qwe
2) asd
3) zxc
#?

在这个脚本执行之后会出现“#?”这个符号来提示你进行选择,选择的内容必须是上边已经列出的那些选项。当选择了一个之后,变量就会变成数字后对应的内容。比如在上面这个脚本中我使用了“c”来当作变量的名字,那么在执行脚本之后,如果我输入“2”并回车,c这个变量中保存的内容就变成了“asd”。


Bash脚本之循环