1.for命令
bash shell提供了for命令,允许你创建一个遍历一系列值的循环。每次迭代都使用其中的一个值来执行已定义好的一组命令,for命令的基本格式如下所示:
1for var in list 2do 3 命令... 4done
在list参数中,需要提供迭代需要的一系列值。可以通过几种不同的方法指定
列表中的值。do和done语句中之间输入的命令可以是一条或者多条标准的bash shell命令。也可以将do语句和for语句放在同一行,但是必须用分号将其同列表中的值分开:for var in list; do
1.1 读取列表中的值
for命令最基本的用法就是遍历for命令自身定义的一系列值,如下例所示:
1#!/bin/bash 2 3for li in A B C D E F G 4do 5 echo The next state is $li 6done 7 8# 结果 9[njust@njust tutorials]$ ./dana.sh 10The next state is A 11The next state is B 12The next state is C 13The next state is D 14The next state is E 15The next state is F 16The next state is G
1.2 读取列表中的复杂值
当shell看到list列表中的单引号并尝试使用它们来定义一个单独的数据值时,结果会一团糟。如下例所示:
1#!/bin/bash 2 3for var in I don't know if this'll work 4do 5 echo "word:$var" 6done 7 8# 结果 9word:I 10word:dont know if thisll 1
两种解决方法:
a.使用转义字符来将单引号进行转义;
b.使用双引号来定义用到单引号的值;
1#!/bin/bash 2 3for var in I don\'t know if "this'll" work 4do 5 echo "word:$var" 6done 7 8# 结果 9[njust@njust tutorials]$ ./dana1.sh 10word:I 11word:don't 12word:know 13word:if 14word:this'll 15word:work
注意:for循环假定每个值都是用空格分割的,如果有包含空格的数据值,就会陷入麻烦。如下例所示:
1#!/bin/bash 2 3for var in New York New Mexico North Carolina 4do 5 echo "Now going to $var" 6done 7 8# 结果 9[njust@njust tutorials]$ ./dana2.sh 10Now going to New 11Now going to York 12Now going to New 13Now going to Mexico 14Now going to North 15Now going to Carolina
for命令用空格来划分列表中的每个值,如果在单独的数据值中有空格,就必须使用双引号将这些值圈起来,在某个值两边使用双引号时,shell并不会将双引号当成值的一部分。如下例所示:
1#!/bin/bash 2 3for var in "New York" "New Mexico" "North Carolina" 4do 5 echo "Now going to $var" 6done 7 8# 结果 9[njust@njust tutorials]$ ./dana2.sh 10Now going to New York 11Now going to New Mexico 12Now going to North Carolina
1.3 从变量读取列表list
通常情况下,是将一系列值都集中存储在一个变量,然后需要遍历变量中的整个列表,如下例所示:
1#!/bin/bash 2 3list="Red Green Blue" 4 5list=$list" Blank" # 使用赋值语句向$list变量包含的已有列表中添加一个值Blank,这是向变量中存储的已有文本字符串尾部添加文本的常用方法 6for var in $list 7do 8 echo "The current color is $var" 9done 10 11# 结果 12[njust@njust tutorials]$ ./dana3.sh 13The current color is Red 14The current color is Green 15The current color is Blue 16The current color is Blank
1.4 从命令中读取值
生成列表中所需值的另一途径就是使用命令的输出。可以使用命令替换来执行任何能产生输出的命令,然后在for命令中使用该命令的输出。如下例所示:
1#!/bin/bash 2 3file="color" 4for c in $(cat $file) 5do 6 echo "beautiful color:$c" 7done 8 9# 结果 10[njust@njust tutorials]$ ./dana4.sh 11beautiful color:Red 12beautiful color:Green 13beautiful color:Bule 14beautiful color:Pink
上述情况中每种颜色都在单独的一行,而不是通过空格分隔的。但是,这并没有解决数据中用空格的情况。
1.5 更改字段分隔符
造成for命令中以空格为分隔符的原因是特殊的环境变量IFS(内部字段分隔符),IFS环境变量定义了bash shell用作字符分隔符的一些列字符。默认情况下,bash shell会将下面的字符当成字段分隔符:
a.空格
b.制表符
c.换行符
如果bash shell在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。这会导致在处理含有空格的数据时,显得十分麻烦。解决方法:可以在shell脚本中临时更改IFS环境变量的值来限制被bash shell当作字段分隔符的字符,如下例所示:
1#!/bin/bash 2 3file="color" 4 5IFS=$'\n' # 告诉bash shell在数据值中忽略空格和制表符,仅识别换行符! 6 7for c in $(cat $file) 8do 9 echo "beautiful color:$c" 10done 11 12# 结果 13[njust@njust tutorials]$ ./dana4.sh 14beautiful color:Red 15beautiful color:Green 16beautiful color:Bule 17beautiful color:Pink 18beautiful color:Deep Purple
在处理代码量较大的脚本时,可能在一个地方需要修改IFS值。在脚本的其他地方继续沿用默认的IFS值。一个可行的方法是在改变IFS之前保存原来的IFS值,之后再恢复它,这样保证了在脚本的后续操作中继续使用的是默认的IFS值。
1IFS.OLD=$IFS 2 3IFS=$'\n' 4<在代码中使用新的IFS值> 5IFS=$IFS.OLD
IFS环境变量的其他一些绝妙用法,假如你要遍历一个文件中用冒号分隔的值(比如/etc/passwd文件),你需要做的就是将IFS设置为冒号IFS=:。
如果要指定多个IFS字符,只要将它们在赋值行串起来就行,如IFS=$'\n':;"。
1.6 用通配符读取目录
可以用for命令来自动遍历目录中的文件,进行此操作时,必须在文件名或路径名中使用通配符。它会强制shell使用文件扩展匹配。文件扩展匹配是生成匹配指定通配符的文件名或路径名的过程。如下例所示:
1#!/bin/bash 2 3for file in /home/njust/tutorials/* 4do 5 6 if [ -d "$file" ] # 在linux中文件名或目录名中包含空格是合法的,要解决这个问题可以将$file变量用双引号圈起来。如果不这样的话遇到含空格的目录名或文件名时会报错! 7 then 8 echo "$file is a directory" 9 elif [ -f "$file" ] 10 then 11 echo "$file is a file" 12 fi 13done 14 15# 结果 16[njust@njust tutorials]$ ./dana5.sh 17/home/njust/tutorials/case.sh is a file 18/home/njust/tutorials/color is a file 19/home/njust/tutorials/curry.sh is a file 20/home/njust/tutorials/dana is a file 21/home/njust/tutorials/dana1.sh is a file 22/home/njust/tutorials/dana2.sh is a file 23/home/njust/tutorials/dana3.sh is a file 24/home/njust/tutorials/dana4.sh is a file 25/home/njust/tutorials/dana5.sh is a file 26/home/njust/tutorials/dana.sh is a file
也可以在for命令中列出多个目录通配符,将目录查找和列表合并进同一个for语句,如下例所示:
1#!/bin/bash 2 3for file in /home/njust/.b* /home/njust/tutorials 4do 5 6 if [ -d "$file" ] 7 then 8 echo "$file is a directory" 9 elif [ -f "$file" ] 10 then 11 echo "$file is a file" 12 else 13 echo "$file doesn't exist" 14 fi 15done 16 17# 结果 18[njust@njust tutorials]$ ./dana6.sh 19/home/njust/.bash_history is a file 20/home/njust/.bash_logout is a file 21/home/njust/.bash_profile is a file 22/home/njust/.bashrc is a file 23/home/njust/tutorials is a directory
2.C语言风格的for命令
bash shell也支持一种for循环,它看起来与C语言风格的for循环类似,但有一些细微的不同。bash中C语言风格的for循环基本格式如下:
1for (( 变量初始化; 条件; 变量迭代变化 ))
注意:有些部分并没有遵循bash shell标准的for命令。如下所示:
a.变量赋值可以有空格;b.条件中的变量不以美元符开头;c.迭代过程的算式未使用expr命令格式;下面是在bash shell程序中使用C语言风格的for命令:
1#!/bin/bash 2 3for (( i = 1; i <= 10; i++ )) 4do 5 echo "The next number is $i" 6done 7 8# 结果 9[njust@njust tutorials]$ ./dana7.sh 10The next number is 1 11The next number is 2 12The next number is 3 13The next number is 4 14The next number is 5 15The next number is 6 16The next number is 7 17The next number is 8 18The next number is 9 19The next number is 10
2.1 使用多个变量
C语言风格的for命令也允许为迭代使用多个变量。循环会单独处理每个变量,可以为每个变量定义不同的迭代过程,尽管可以使用多个变量,但只能在for循环中定义一种条件。
1#!/bin/bash 2 3# mutiple variables 4 5for (( a = 1, b = 10; a <= 10; a++, b-- )) 6do 7 echo "$a - $b" 8done 9 10# 结果 11[njust@njust tutorials]$ ./dana8.sh 121 - 10 132 - 9 143 - 8 154 - 7 165 - 6 176 - 5 187 - 4 198 - 3 209 - 2 2110 - 1
3.while命令
while命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的测试命令返回的是退出状态码0,它会在每次迭代的一开始测试test命令。在test命令返回非零退出状态码时,while命令会停止执行那组命令。
3.1 while的基本格式
1while test 命令 2do 3 命令 4done
while命令的关键点:所指定的test 命令的退出状态码必须随着循环中运行的命令而改变。如果退出状态码不发生改变,while循环就会一直不停地执行下去。最常见的test 命令的用法是用方括号检查循环命令中用到的shell变量的值,如下所示:
1#!/bin/bash 2 3var1=10 4while [ $var1 -gt 0 ] 5do 6 echo $var1 7 var1=$[ $var1 - 1 ] 8done 9 10# 结果 11[njust@njust tutorials]$ ./dana9.sh 1210 139 148 157 166 175 184 193 202 211
3.2 使用多个测试命令
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。如下例所示:
1#!/bin/bash 2 3var=10 4 5while echo $var 6 [ $var -ge 0 ] 7do 8 echo "This is inside in the loop" 9 var=$[ $var - 1 ] 10done 11 12# 结果 13[njust@njust tutorials]$ ./dana10.sh 1410 15This is inside in the loop 169 17This is inside in the loop 188 19This is inside in the loop 207 21This is inside in the loop 226 23This is inside in the loop 245 25This is inside in the loop 264 27This is inside in the loop 283 29This is inside in the loop 302 31This is inside in the loop 321 33This is inside in the loop 340 35This is inside in the loop 36-1
在含有多个命令的while语句中,在每次迭代中所有测试命令都会被执行,包括测试命令失败的最后一次迭代。注意:指定多个测试命令时,每个测试命令都出现在单独的一行。
4.until命令
until命令与while命令工作方式相反,until命令要求你指定一个通常返回非零状态码的测试命令。只有测试命令的退出状态码非零,bash shell才会执行循环中列出的命令。一旦测试命令返回退出状态码0,循环就结束了。until命令的基本格式如下所示:
1until test 命令 2do 3 命令 4done
可以在until命令语句中放入多个测试命令,只有最后一个命令的退出状态码决定了bash shell是否执行已定义的命令。如下例所示:
1#!/bin/bash 2 3var=100 4 5until [ $var -eq 0 ] 6do 7 echo $var 8 var=$[ $var - 25 ] 9done 10 11# 结果 12[njust@njust tutorials]$ ./dana11.sh 13100 1475 1550 1625
与while命令相似,until命令在使用多个测试命令时要注意。shell会执行指定的多个测试命令,只有在最后一个命令成立时才会停止。如下例所示:
1#!/bin/bash 2 3var=100 4 5until echo $var 6 [ $var -eq 0 ] 7do 8 echo "Inside the loop: $var" 9 var=$[ $var - 25 ] 10done 11 12# 结果 13[njust@njust tutorials]$ ./dana12.sh 14100 15Inside the loop: 100 1675 17Inside the loop: 75 1850 19Inside the loop: 50 2025 21Inside the loop: 25 220
5.嵌套循环
循环语句可以在循环内使用任意类型的命令,包括其他循环命令。这种循环称为嵌套循环。注意:使用嵌套循环时,在迭代中使用迭代,与命令运行的次数是乘积关系。如下例所示:
1#!/bin/bash 2 3for (( a = 1; a <= 3; a++ )) 4do 5 echo "Starting loop $a:" 6 for (( b = 1; b <= 3; b++ )) 7 do 8 echo " Inside loop:$b" 9 done 10done 11 12# 结果 13[njust@njust tutorials]$ ./dana13.sh 14Starting loop 1: 15Inside loop:1 16Inside loop:2 17Inside loop:3 18Starting loop 2: 19Inside loop:1 20Inside loop:2 21Inside loop:3 22Starting loop 3: 23Inside loop:1 24Inside loop:2 25Inside loop:3
在混用循环命令时也一样,比如在while循环内部放置一个for循环。如下例所示:
1#!/bin/bash 2 3var1=5 4 5while [ $var1 -ge 0 ] 6do 7 echo "Outer loop: $var1" 8 for (( var2 = 1; $var2 < 3; var2++ )) 9 do 10 var3=$[ $var1 * $var2 ] 11 echo " Inner loop: $var1 * $var2 = $var3" 12 done 13 var1=$[ $var1 - 1 ] 14done 15 16# 结果 17[njust@njust tutorials]$ ./dana14.sh 18Outer loop: 5 19Inner loop: 5 * 1 = 5 20Inner loop: 5 * 2 = 10 21Outer loop: 4 22Inner loop: 4 * 1 = 4 23Inner loop: 4 * 2 = 8 24Outer loop: 3 25Inner loop: 3 * 1 = 3 26Inner loop: 3 * 2 = 6 27Outer loop: 2 28Inner loop: 2 * 1 = 2 29Inner loop: 2 * 2 = 4 30Outer loop: 1 31Inner loop: 1 * 1 = 1 32Inner loop: 1 * 2 = 2 33Outer loop: 0 34Inner loop: 0 * 1 = 0 35Inner loop: 0 * 2 = 0