正文
1、for 命令
bash shell 提供了 for 命令,允许你创建一个遍历一系列值的循环。每次迭代都使用其中一个值来执行已定义好的一组命令,
for var in list do command done
在 list 参数中,你需要提供迭代中需要用到的一系列值,
- 在每次迭代中,变量 var 会包含列表中的当前值。第一次迭代会使用列表中的第一个值,第二次迭代使用第二个值,以此类推,知道列表中的所有值都过一遍,
- 在 do 和 done 语句之间输入的命令可以是一条或多条标准的 bash shell 命令。在这些命令中,$var 变量包含着这次迭代对应的当前列表项中的值。
1.1 读取列表中的值
for 命令最基本的用法就是遍历 for 命令自身所定义的一系列值
#!/bin/bash # Program: # basic for command # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for test in Alabama Arizona Arkansas California Colorado do echo "The next state is ${test}" done exit 0
1.2 读取列表中的复杂值
shell 看到了列表值中的单引号并尝试使用它们来定义一个单独的数据值,这真是把事情搞得一团糟。有两种方法可解决这个问题:
- 使用转义字符(反斜线)来将单引号转移;
- 使用双引号来定义用到单引号的值。
#!/bin/bash # Program: # another example of how to use the for command # History: # 2021/12/18 junfenghe.cloud@q.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for test in I don\'t know if "this'll" work do echo "word: ${test}" done exit 0
1.3 从变量读取列表
通常 shell 脚本遇到的情况是,你将一系列值都集中存储在了一个变量中,然后需要遍历变量中的整个列表。
#!/bin/bash # Program: # using a variable to hold the list # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path list="ALdjk djlsgkd dgskjn dsjgdkj dhn dsdsdn" list=${list}" dingdsl" for item in ${list} do echo "Have you ever visited ${item}" done exit 0
1.4 更改字段分隔符
内部字段分隔符(internal field separator),IFS 环境变量定义了 bash shell 用作字段分隔符的一系列字符。默认情况下,bash shell 会将下列字段当做字段分隔符,
- 空格
- 制表符
- 换行符
下面代码中所引文件的内容
#!/bin/bash # Program: # reading values from a file path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path file="states" IFS_OLD=${IFS} IFS=$'\n' for state in $(cat ${file}) do echo "Visit beautiful ${state}" done IFS=${IFS_OLD} exit 0
1.5 用通配符读取目录
可以用 for 命令来自动遍历目录中的文件。进行此操作时,必须在文件名或路径名中使用通配符。它会强制 shell 使用使用文件扩展匹配。文件扩展匹配是生成匹配指定通配符的文件名或路径名的过程。
在 Linux 中,目录名和文件名中包含空格当然是合法的。要适应这种情况,应该将变量用双引号圈起来。如果不这么做,遇到含有空格的目录名或文件名时就会有错误产生。
#!/bin/bash # Program: # iterating through multiple directires # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for file in /home/qij/.b* do if [ -d "${file}" ] then echo "${file} is a directory" elif [ -f "${file}" ] then echo "${file} is a file" else echo "${file} does exist" fi done exit 0
2、while 命令
1.1 while 的基本格式
while 的基本格式,
while test command do other commands done
#!/bin/bash # Program: # while command test # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path val1=10 while [ ${val1} -gt 0 ] do echo ${val1} (( val1=${val1} - 1 )) done exit 0
3、until 命令
until 命令和 while 命令工作的方式完全相反。until 命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为 0,bash shell 才会执行循环中列出的命令,一旦测试命令返回了退出状态 0,循环就结束了。
#!/bin/bash # Program: # using the until command # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path val1=100 until [ ${val1} -eq 0 ] do echo "${val1}" val1=$(( ${val1} - 25 )) done exit 0
4、嵌套循环
循环语句可以在循环内使用任意类型的命令,包括其他循环命令。这种循环叫做嵌套循环(nested loop)。注意,在使用嵌套循环时,你是在迭代中使用迭代,与命令运行的次数是乘积关系。不注意这点的话,有可能会在脚本中造成问题。
#/bin/bash # Program: # nesting for loops # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for (( a = 1; a <= 3; a++ )) do echo "Starting loop ${a}" for (( b = 1;b <=3; b++ )) do echo " Inside loop: ${b}" done done exit 0
#!/bin/bash # Program: # placing a for loop inside a while loop # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path var1=5 while [ ${var1} -ge 0 ] do echo "Outer loop: ${var1}" for (( var2 = 1; var2 < 3; var2++ )) do var3=$(( ${var1} * ${var2} )) echo " Inside loop: ${var1} * ${var2} = ${var3}" done var1=$(( ${var1} - 1 )) done exit 0
#!/bin/bash # Program: # using until and while loops path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path 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 exit 0
5、循环处理文件数据
通常必须遍历存储在文件中的数据。这要求结合已经讲过的两种技术:
- 使用嵌套循环
- 修改 IFS 环境变量
通过修改 IFS 环境变量,就能强制 for 命令将文件中的每行都当成一个条目来处理,即便数据中有空格也是如此。一旦从文件中提取出了单独的行,可能需要再次利用循环来提取行中的数据。
#!/bin/bash # Program: # changing the IFS value # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path 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 exit 0
6、循环控制
6.1 break 命令
break 命令是退出循环的一个简单方法。可以用 break 命令来退出任意类型的循环,包括 while 和 until 循环。
#!/bin/bash # Program: # breaking out of an outer loop # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path 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 exit 0
PS: 上述代码里的 break n,其中 n 指定了要跳出的循环层数。默认情况下,为 1,表名跳出的是当前的循环。如果你将 n 设为 2,break 命令就会停止下一级的外部循环。
6.2 continue 命令
continue 命令可以提前中止某次循环中的命令,但并不会完全中止整个循环。可以在循环内部设置 shell 不执行命令的条件。
#!/bin/bash # Program: # using the continue command # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for (( var1 = 1; var1 < 15; var1++ )) do if [ ${var1} -gt 5 ] && [ ${var1} -lt 10 ] then continue fi echo "Iteration number: ${var1}" done exit 0
7、处理循环的输出
在 shell 脚本中,你可以对循环的输出使用管道或进行重定向。这可以通过在 done 命令之后添加一个处理命令来实现,
#!/bin/bash # Program: # redirecting the for output to a file # History: # 2021/12/18 junfenghe.cloud@qq.com version:0.0.1 path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export path for (( a = 1; a < 10; a++ )) do echo "The number is ${a}" done > test123.txt echo "The command is finished." exit 0
以上算是我今天整整一天的简单练习、简书输出、工作输出的一部分。其实学到这里的时候,我工作中要编写的一键部署自动化脚本已经优化的差不多了,还剩一点,后面接着去解决。
(其实此间最有成就感的还是工作中要写的那个一键部署自动化脚本,百把来行 shell 脚本,感觉就是洋洋洒洒的非常帅气,就是不便发出来,只能内部用。)