1for命令
for命令的基本格式:
for var in list do commands done
在list参数中,你需要提供迭代中要用到的一系列值。
1.1读取列表中的值
例子:
$ vim test1 #!/bin/bash # testing the for variable after the looping for test in Alabama Alaska Arizona Arkansas California Colorado do echo "The next state is $test" done echo "The last state we visited was $test" test=Connecticut echo "Wait, now we're visiting $test" 执行结果: $ ./test1 The next state is Alabama The next state is Alaska The next state is Arizona The next state is Arkansas The next state is California The next state is Colorado The last state we visited was Colorado Wait, now we're visiting Connecticut
1.2读取列表中的复杂值
看这个例子:
$ vim badtest1 #!/bin/bash # another example of how not to use the for command for test in I don't know if this'll work do echo "word:$test" done 结果: $ ./badtest1 word:I word:dont know if thisll word:work
为什么会是这样呢?怎么处理?有两种办法:
- 使用转义字符(反斜线)来将单引号转义;
- 使用双引号来定义用到单引号的值。
处理之后的shell脚本:
$ vim test2 #!/bin/bash # another example of how not to use the for command for test in I don\'t know if "this'll" work do echo "word:$test" done 结果: $ ./test2 word:I word:don't word:know word:if word:this'll word:work
for循环每个值都是用空格分割的。如果有包含空格的数据值,也会导致这种类似的问题,所以处理方法可以采用上面的第二种(加引号)的方式。
1.3从变量读取列表
看例子:
$ vim test4 #!/bin/bash # using a variable to hold the list list="Alabama Alaska Arizona Arkansas Colorado" list=$list" Connecticut" for state in $list do echo "Have you ever visited $state?" done 结果: $ ./test4 Have you ever visited Alabama? Have you ever visited Alaska? Have you ever visited Arizona? Have you ever visited Arkansas? Have you ever visited Colorado? Have you ever visited Connecticut?
1.4从命令读取值
$ vim test5 #!/bin/bash # reading values from a file file="states" for state in $(cat $file) do echo "Visit beautiful $state" done $ cat states Alabama Alaska Arizona Arkansas Colorado Connecticut Delaware Florida Georgia 执行结果: $ ./test5 Visit beautiful Alabama Visit beautiful Alaska Visit beautiful Arizona Visit beautiful Arkansas Visit beautiful Colorado Visit beautiful Connecticut Visit beautiful Delaware Visit beautiful Florida Visit beautiful Georgia
1.5更改字段分隔符
IFS叫作内部字段分隔符
IFS环境变量定义了bash shell用作字段分隔符的一系列字符。默认情况下,bash shell会将下列字符当作字段分隔符:
- 空格
- 制表符
- 换行符
如果bash shell在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。
但是我们可以在shell脚本中临时更改IFS环境变量的值来限制被bash shell当作字段分隔符的字符。例如,如果你想修改IFS的值,使其只能识别换行符,那就必须这么做:
IFS=$'\n'
将这个语句加入到脚本中,告诉bash shell在数据值中忽略空格和制表符。对前一个脚本使用这种方法,将获得如下输出。
$ vim test5b #!/bin/bash # reading values from a file file="states" IFS=$'\n' for state in $(cat $file) do echo "Visit beautiful $state" done $ ./test5b Visit beautiful Alabama Visit beautiful Alaska Visit beautiful Arizona Visit beautiful Arkansas Visit beautiful Colorado Visit beautiful Connecticut Visit beautiful Delaware Visit beautiful Florida Visit beautiful Georgia Visit beautiful New York Visit beautiful New Hampshire Visit beautiful North Carolina
在处理代码量较大的脚本时,可能在一个地方需要修改IFS的值,然后忽略这次修改,在脚本的其他地方继续沿用IFS的默认值。一个可参考的安全实践是在改变IFS之前保存原来的IFS值,之后再恢复它。 这种技术可以这样实现:
IFS.OLD=$IFS IFS=$'\n' <在代码中使用新的IFS值> IFS=$IFS.OLD
这就保证了在脚本的后续操作中使用的是IFS的默认值。
此外,我们还可以这样做,假如要做的是将IFS的值设为冒号:
IFS=:
如果要指定多个IFS字符,只要将它们在赋值行串起来就行。
IFS=$'\n':;"
这个赋值会将换行符、冒号、分号和双引号作为字段分隔符。如何使用IFS字符解析数据没有任何限制。
1.6用通配符读取目录
$ vim test6 #!/bin/bash # iterate through all the files in a directory for file in /home/rich/test/* do if [ -d "$file" ] then echo "$file is a directory" elif [ -f "$file" ] then echo "$file is a file" fi done $ ./test6 /home/rich/test/dir1 is a directory /home/rich/test/myprog.c is a file /home/rich/test/myprog is a file /home/rich/test/myscript is a file /home/rich/test/newdir is a directory /home/rich/test/newfile is a file /home/rich/test/newfile2 is a file /home/rich/test/testdir is a directory /home/rich/test/testing is a file /home/rich/test/testprog is a file /home/rich/test/testprog.c is a file
for命令会遍历/home/rich/test/*输出的结果。该代码用test命令测试了每个条目(使用方括号方法),以查看它是目录还是文件。for后面可以接多个需要循环遍历的路径。注意这里的判断条件“$file”用引号包围起来了,这是为了避免我们目录名和文件名有空格而导致误认为有多个文件而出错。
2 C语言风格的for命令
例一:使用单一变量
$ vim test8 #!/bin/bash # testing the C-style for loop for (( i=1; i <= 10; i++ )) do echo "The next number is $i" done $ ./test8 The next number is 1 The next number is 2 The next number is 3 The next number is 4 The next number is 5 The next number is 6 The next number is 7 The next number is 8 The next number is 9 The next number is 10
例二:使用多个变量
$ vim test9 #!/bin/bash # multiple variables for (( a=1, b=10; a <= 10; a++, b-- )) do echo "$a - $b" done $ ./test9 1 - 10 2 - 9 3 - 8 4 - 7 5 - 6 6 - 5 7 - 4 8 - 3 9 - 2 10 - 1
3while命令
while命令的格式是:
while test command do other commands done
while命令的关键在于所指定的test command的退出状态码必须随着循环中运行的命令而改变。如果退出状态码不发生变化, while循环就将一直不停地进行下去。
$ vim test10 #!/bin/bash # while command test var1=5 while [ $var1 -gt 0 ] do echo $var1 var1=$[ $var1 - 1 ] done $ ./test10 5 4 3 2 1
使用多个测试命令:
$ vim test11 #!/bin/bash # testing a multicommand while loop var1=5 while echo $var1 [ $var1 -ge 0 ] do echo "This is inside the loop" var1=$[ $var1 - 1 ] done $ ./test11 5 This is inside the loop 4 This is inside the loop 3 This is inside the loop 2 This is inside the loop 1 This is inside the loop 0 This is inside the loop -1
while语句中定义了两个测试命令。
while echo $var1 [ $var1 -ge 0 ]
第一个测试简单地显示了var1变量的当前值。第二个测试用方括号来判断var1变量的值。在循环内部,echo语句会显示一条简单的消息,说明循环被执行了。注意当你运行本例时输出是如何结束的。
This is inside the loop -1
while循环会在var1变量等于0时执行echo语句,然后将var1变量的值减一。接下来再次执行测试命令,用于下一次迭代。echo测试命令被执行并显示了var变量的值(现在小于0了)。直到shell执行test测试命令,whle循环才会停止。
4until命令
until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。
格式:
until test commands do other commands done
下面是使用until命令的一个例子。
$ vim test12 #!/bin/bash # using the until command var1=100 until [ $var1 -eq 0 ] do echo $var1 var1=$[ $var1 - 25 ] done $ ./test12 100 75 50 25
使用多个测试命令与while一样。
5嵌套循环
在for循环中嵌套for循环的简单例子:
$ vim test14 #!/bin/bash # nesting for loops for (( a = 1; a <= 3; a++ )) do echo "Starting loop $a:" for (( b = 1; b <= 3; b++ )) do echo " Inside loop: $b" done done $ ./test14 Starting loop 1: Inside loop: 1 Inside loop: 2 Inside loop: 3 Starting loop 2: Inside loop: 1 Inside loop: 2 Inside loop: 3 Starting loop 3: Inside loop: 1 Inside loop: 2 Inside loop: 3
在while循环内部放置一个for循环:
$ vim test15 #!/bin/bash # placing a for loop inside a while loop var1=5 while [ $var1 -ge 0 ] do echo "Outer loop: $var1" for (( var2 = 1; $var2 < 3; var2++ )) do var3=$[ $var1 * $var2 ] echo " Inner loop: $var1 * $var2 = $var3" done var1=$[ $var1 - 1 ] done $ ./test15 Outer loop: 5 Inner loop: 5 * 1 = 5 Inner loop: 5 * 2 = 10 Outer loop: 4 Inner loop: 4 * 1 = 4 Inner loop: 4 * 2 = 8 Outer loop: 3 Inner loop: 3 * 1 = 3 Inner loop: 3 * 2 = 6 Outer loop: 2 Inner loop: 2 * 1 = 2 Inner loop: 2 * 2 = 4 Outer loop: 1 Inner loop: 1 * 1 = 1 Inner loop: 1 * 2 = 2 Outer loop: 0 Inner loop: 0 * 1 = 0 Inner loop: 0 * 2 = 0
混用until和while循环:
$ vim test16 #!/bin/bash # using until and while loops var1=3 until [ $var1 -eq 0 ] do echo "Outer loop: $var1" var2=1 while [ $var2 -lt 5 ] do var3=$(echo "scale=4; $var1 / $var2" | bc) echo " Inner loop: $var1 / $var2 = $var3" var2=$[ $var2 + 1 ] done var1=$[ $var1 - 1 ] done $ ./test16 Outer loop: 3 Inner loop: 3 / 1 = 3.0000 Inner loop: 3 / 2 = 1.5000 Inner loop: 3 / 3 = 1.0000 Inner loop: 3 / 4 = .7500 Outer loop: 2 Inner loop: 2 / 1 = 2.0000 Inner loop: 2 / 2 = 1.0000 Inner loop: 2 / 3 = .6666 Inner loop: 2 / 4 = .5000 Outer loop: 1 Inner loop: 1 / 1 = 1.0000 Inner loop: 1 / 2 = .5000 Inner loop: 1 / 3 = .3333 Inner loop: 1 / 4 = .2500
6循环处理文件数据
通常必须遍历存储在文件中的数据。这要求结合已经讲过的两种技术:
- 使用嵌套循环
- 修改IFS环境变量
通过修改IFS环境变量,就能强制for命令将文件中的每行都当成单独的一个条目来处理,即便数据中有空格也是如此。一旦从文件中提取出了单独的行,可能需要再次利用循环来提取行中的数
典型的例子是处理/etc/passwd文件中的数据。这要求你逐行遍历/etc/passwd文件,并将IFS 变量的值改成冒号,这样就能分隔开每行中的各个数据段了:
#!/bin/bash # changing the IFS value IFS.OLD=$IFS IFS=$'\n' for entry in $(cat /etc/passwd) do echo "Values in $entry –" IFS=: for value in $entry do echo " $value" done done
这个脚本使用了两个不同的IFS值来解析数据。第一个IFS值解析出/etc/passwd文件中的单独的行。内部for循环接着将IFS的值修改为冒号,允许你从/etc/passwd的行中解析出单独的值。
7控制循环
7.1break命令
1. 跳出单个循环
用for循环举例,当然break命令同样适用于while和until循环:
$ vim test17 #!/bin/bash # breaking out of a for loop for var1 in 1 2 3 4 5 6 7 8 9 10 do if [ $var1 -eq 5 ] then break fi echo "Iteration number: $var1" done echo "The for loop is completed" $ ./test17 Iteration number: 1 Iteration number: 2 Iteration number: 3 Iteration number: 4 The for loop is completed
2. 跳出内部循环
在处理多个循环时,break命令会自动终止你所在的 内层的循环。
3. 跳出外部循环
有时你在内部循环,但需要停止外部循环。break命令接受单个命令行参数值:
break n
其中n指定了要跳出的循环层级。默认情况下,n为1,表明跳出的是当前的循环。如果你将n设为2,break命令就会停止下一级的外部循环。
$ vim test20 #!/bin/bash # breaking out of an outer loop for (( a = 1; a < 4; a++ )) do echo "Outer loop: $a" for (( b = 1; b < 100; b++ )) do if [ $b -gt 4 ] then break 2 fi echo " Inner loop: $b" done done $ ./test20 Outer loop: 1 Inner loop: 1 Inner loop: 2 Inner loop: 3 Inner loop: 4
7.2continue命令
continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。可以在循环内部设置shell不执行命令的条件。
一个在for循环中使用continue命令的简单例子:
$ vim test21 #!/bin/bash # using the continue command for (( var1 = 1; var1 < 9; var1++ )) do if [ $var1 -gt 3 ] && [ $var1 -lt 6 ] then continue fi echo "Iteration number: $var1" done $ ./test21 Iteration number: 1 Iteration number: 2 Iteration number: 3 Iteration number: 6 Iteration number: 7 Iteration number: 8
同样地,continue命令也有像break一样的另外两中用法,我们来看一下
continue n
8处理循环的输出
在shell脚本中,你可以对循环的输出使用管道或进行重定向。这可以通过在done命令之后添加一个处理命令来实现。
例一:
for file in /home/rich/* do if [ -d "$file" ] then echo "$file is a directory" elif echo "$file is a file" fi done > output.txt
shell会将for命令的结果重定向到文件output.txt中,而不是显示在屏幕上。
例二:
$ vim test24 #!/bin/bash for state in "North Dakota" Connecticut Illinois Alabama Tennessee do echo "$state is the next place to go" done | sort echo "This completes our travels" $ ./test24 Alabama is the next place to go Connecticut is the next place to go Illinois is the next place to go North Dakota is the next place to go Tennessee is the next place to go This completes our travels
state值并没有在for命令列表中以特定次序列出。for命令的输出传给了sort命令,该命令会改变for命令输出结果的顺序。运行这个脚本实际上说明了结果已经在脚本内部排好序了。
9实例
9.1查找可执行文件
当你从命令行中运行一个程序的时候,Linux系统会搜索一系列目录来查找对应的文件。这些目录被定义在环境变量PATH中。如果你想找出系统中有哪些可执行文件可供使用,只需要扫描PATH环境变量中所有的目录就行了。
$ vim test25 #!/bin/bash # finding files in the PATH IFS=: for folder in $PATH do echo "$folder:" for file in $folder/* do if [ -x $file ] then echo " $file" fi done done
9.2创建多个用户账户
你不用为每个需要创建的新用户账户手动输入useradd命令,而是可以将需要添加的新用户账户放在一个文本文件中,然后创建一个简单的脚本进行处理。这个文本文件的格式如下:
userid,name
第一个条目是你为新用户账户所选用的用户ID。第二个条目是用户的全名。两个值之间使用逗号分隔,这样就形成了一种名为逗号分隔值的文件格式(或者是.csv)。
$ vim test26 #!/bin/bash # process new user accounts input="users.csv" while IFS=',' read -r userid name do echo "adding $userid" useradd -c "$name" -m $userid done < "$input"
运行这个脚本必须作为root用户,因为useradd命令需要root权限。
来源:https://www.cnblogs.com/ericz2j/p/12045569.html