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 脚本,感觉就是洋洋洒洒的非常帅气,就是不便发出来,只能内部用。)