shell脚本的循环
文章目录
- shell脚本的循环
- while do done、until do done(不定循环)
- 例题1
- 例题2
- 例题3
- 例题4
- for...do...done(固定循环)
- 例题1
- 例题2
- 例题3
- 例题4
- for...do...done的数值处理
- 例题
- 搭配随机数与数组的实验
- 例题1
- 例题2
- shell脚本的跟踪与调试
- 使用案例
shell脚本的循环
除了if…then…fi 这种条件判断式之外,循环可能是程序当中最重要的一环了。循环可以不断地执行某个程序段落,直到用户设置的条件完成为止。所以,重点是那个【条件的完成】是什么,除了这种依据判断式完成与否的不定循环之外,还有另外一种已经固定要跑多少次循环状态,可称为固定循环状态。
while do done、until do done(不定循环)
一般来说,不定循环最常见的就是下面的两种状态了:
while [ condition ] # 中括号内的状态就是判断式
do # do是循环的开始
程序段
done # done 是循环的结束
while的中文是【当…时】,所以,这种方式说的是【当condition条件成立时,就进行循环,直到condition的条件不成立才停止】的意思,还有另外一种不定循环的方式
until [ condition ]
do
程序段
done
这种方式恰恰与while相反,它说的是【当condition条件成立时,就终止循环,否则就持续进行循环的程序段】
例题1
假设我要让用户输入yes或是YES才结束程序的执行,否则就一直告诉用户输入字符串
#!/bin/bash
# 程序说明:
# 输入yes/YES停止输出字符串
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
read -p "请输入yes/YES来停止该程序:" yn
done
echo -e "\n您已经停止了该程序\a"
上面这个例题当中【当 ${yn} 这个变量不是 “yes” 且 ${yn} 也不是 "YES"时,就进行循环的程序】,如果输入了yes 或YES 就退出循环。
例题2
我们改变一下上述案例使用 until do done 的形式循环程序
#!/bin/bash
# 程序说明:
# 输入yes/YES停止输出字符串
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
until [ "${yn}" == "yes" -o "${yn}" == "YES" ]
do
read -p "请输入yes/YES来停止该程序:" yn
done
echo -e "\n您已经停止了该程序\a"
上面这个例题当中【当 ${yn} 这个变量是"YES" 或是 “yes” 就退出循环】否则就持续进行循环。
例题3
如果我要计算1+2+…+100的结果呢?利用循环
#!/bin/bash
# 程序说明:
# 求1+...+100的和是多少
# 时间:
# 2023/05/04
PAHT=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
s=0
i=0
while [ "${i}" != "100" ]
do
i=$(($i+1))
s=$(($s+$i))
done
echo "1+2+3...+100=${s}"
当你的执行结果为5050这个数据时就对了。
例题4
如果让用户自行输入一个数字,让程序1+2+3…加到你输入的数字为止,该如何编写呢?
#!/bin/bash
# 程序说明:
# 求1+...+到用户输入的那个数为止,求和。
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
read -p "请输入一个数字我将从1+到你输入的这个数字为止:" stdin
s=0
i=0
while [ "${i}" != "${stdin}" ]
do
i=$((${i}+1))
s=$((${i}+${s}))
done
echo "1+2+3...加到你输入的这个数字,和为:${s} "
是不是很简单,也可以使用until do done 来测试一下
for…do…done(固定循环)
相对于while、until的循环方式是必须要【符合某个条件】的状态,for这种语法,则是【已经知道要进行几次循环】的状态
它的语法为
for var in con1 con2 con3 .....
do
程序段
done
以上面的例子来说,这个${var}的变量内容在循环工作时:
- 第一次循环时,${var} 的内容为 con1
- 第二次循环时,${var} 的内容为 con2
- 第三次循环时,${var} 的内容为 con3
- …
- …
例题1
假设我有3种动物,分别是dog、cat、sheep(🐏),我想每一行都输出这样:【There are dogs…】之类的字样
#!/bin/bash
# 程序说明:
# 该程序判断动物园有哪些动物
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
echo -e "今天的动物园有哪些动物呢?"
for animal in dog cat tiger
do
echo "有${animal}s"
done
例题2
如果我想要找到/etc/passwd 内的第一个字段,能不能通过管道命令的cut识别出单纯的账号名称后,以id分别检查用户的标识符与特殊参数(id 用户名)?
#!/bin/bash
# 程序说明:
# 该程序识别passwd的账户名称后,用id 账户名称查看用户标识符与特殊参数
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
cutpasswd=$(cut -d ':' -f1 /etc/passwd ) # 选取账号名称
for username in ${cutpasswd} # 开始循环
do
id ${username}
done
执行结果如下
[root@localhost shelldir]# sh userid.sh
uid=0(root) gid=0(root) 组=0(root)
uid=1(bin) gid=1(bin) 组=1(bin)
uid=2(daemon) gid=2(daemon) 组=2(daemon)
uid=3(adm) gid=4(adm) 组=4(adm)
uid=4(lp) gid=7(lp) 组=7(lp)
uid=5(sync) gid=0(root) 组=0(root)
uid=6(shutdown) gid=0(root) 组=0(root)
uid=7(halt) gid=0(root) 组=0(root)
uid=8(mail) gid=12(mail) 组=12(mail)
uid=11(operator) gid=0(root) 组=0(root)
uid=12(games) gid=100(users) 组=100(users)
.....
...
例题3
假如我利用ping这个可以判断网络状态的命令,来进行网络状态的实际检测时,我想要检测的域名是本机所在的192.168.124.1~192.168.124.100网段,1~100 ,总不会我在for后面输入 1到100吗?
#!/bin/bash
# 程序说明:
# 该程序是检测网段的
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:~/shelldir
export PATH
network="192.168.124" # 定义一个域名
for segment in $(seq 1 100) # 循环 1~100的数字
do
ip="${network}.${segment}" # 通过拼接得到一个完整的IP
ping -c 1 -w 1 ${ip} > /dev/null 2>&1 # ping命令检测当前IP地址的网络状态
if [ "$?" -eq "0" ]; then # 如果返回0,则表示主机正常响应
echo "${ip}是UP的"
else
echo "${ip}是DOWN的"
fi # 别忘了加fi
done # 和done
上面这一串命令执行后就可以显示出192.168.124.1~192.168.124.100共100台主机目前是否能够与这个太机器互通。如果你的network和我的不一样修改一下就行了,案例中的$(seq 1 100)是连续输出 1~100的数字,也可以写为【{1…100}】
例题4
我想要让用户输入某个目录文件名,然后我找出某目录内的文件名权限,应该怎么做呢?
#!/bin/bash
# 程序说明:
# 该程序是查看目录下的文件权限的
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:~/shelldir
export PATH
read -p "请输入目录文件名:" dir
if [ "${dir}" == "" -o ! -d "${dir}" ]; then
echo "该${dir}目录不存在,请重新输入"
exit 1
fi
filelist=$(ls ${dir}) # 列出所有该目录下的文件名称
for filename in ${filelist}
do
perm=""
test -r "${dir}/${filename}" && perm="${perm}可读"
test -w "${dir}/${filename}" && perm="${perm}可写"
test -x "${dir}/${filename}" && perm="${perm}可执行"
echo "这个文件 ${dir}/${filename} 权限是${perm}"
done
for…do…done的数值处理
除了上述方法之外,for循环还有另外一种写法,语法如下:
for (( 初始值; 限制值; 赋值运算 ))
do
程序段
done
这种语法适合于数值方面的运算当中,for后面括号内的三串内容意义是:
- 初始值:某个变量在循环当中的起始值,直接类似 i=1 设置好;
- 限制值:当变量的值在这个限制值的范围内,就继续进行循环,例如 i<=100
- 赋值运算:每做一次循环,变量也变化,例如:i=i+1
值得注意的是,在【赋值运算】的设置上,如果每次增加1,则可以使用类似【i++】的方式,就是i每次循环都会增加1的意思。
例题
从1累加到用户输入的数值
#!/bin/bash
# 程序说明:
# 该程序是让用户输入个数字然后累加到用户输入的那个数字,求和
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:~/shelldir
export PATH
read -p "请输入一个数字我会从1+..累加到你输入的那个数字:" usershuzi
s=0
for (( i=1; i<=${usershuzi}; i=i+1 ))
do
s=$((${s}+${i}))
done
echo "1+2+3....+${usershuzi}=${s}"
搭配随机数与数组的实验
说到随机数肯定会用到系统给我提供的这个变量${RANDOM}
${RANDOM} 是一个 Bash 内置的环境变量,用于生成一个随机整数。每次调用 ${RANDOM} 时,都会生成一个 0 到 32767之间的随机整数。
可以使用以下方式来获取 ${RANDOM} 的值:
echo ${RANDOM}
也可以将 ${RANDOM} 的值赋值给变量:
my_random=${RANDOM}
echo ${my_random}
由于 ${RANDOM} 只是一个环境变量,所以它的值只在当前 Shell 进程中有效。如果需要在脚本中生成多个随机数,可以在需要的地方调用 ${RANDOM}。
例题1
假如你在家,你不知道吃什么饭,选择困难就很烦,那你就可以写个脚本,脚本搭配随机数来告诉你,今天中午吃啥好?执行这个脚本后,直接跟你说要吃什么。
应该怎么做呢?首先你得要将全部的店家输入到一组数组当中,再通过随机数的处理,去获取可能的数值,再将搭配到的数值显示出来即可。
#!/bin/bash
# 程序说明:
# 打印今天中午吃什么饭
# 时间:
# 2023/05/04
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:~/shelldir
export PATH
eat[1]="红烧肉" # 定义一个数组
eat[2]="糖醋排骨"
eat[3]="小炒牛肉"
eat[4]="小炒五花肉"
eat[5]="平菇炒香干"
eat[6]="香菇炒芹菜"
eat[7]="喝西北风"
eat[8]="奥里给"
eat[9]="泡面"
eatnum=9 # 定义变量9,表示午餐可选的菜品
check=$(( ${RANDOM} * ${eatnum} /32767 +1)) # 通过随机数计算出今天中午吃什么菜
echo "你中午吃${eat[${check}]}"
上面案例中最重要的就是随机数了,【${RANDOM} * ${eatnum} /32767 +1 】计算出 check 变量的值,${RANDOM} 表示 Bash 内置的环境变量,用于生成一个随机整数,每次调用 ${RANDOM} 时,都会生成一个 0 到 32767 之间的随机整数,这里将其乘以菜品数量,再除以 32767,最后加 1,得到一个 1 到 9 之间的随机整数。
当我们执行上述案例时,就知道自己要吃啥了非常的方便。
例题2
那么如果我想吃3个菜呢?而且不能重复一样的,那应该怎么做?
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:~/shelldir
export PATH
eat[1]="红烧肉"
eat[2]="糖醋排骨"
eat[3]="小炒牛肉"
eat[4]="小炒五花肉"
eat[5]="平菇炒香干"
eat[6]="香菇炒芹菜"
eat[7]="喝西北风"
eat[8]="奥里给"
eat[9]="泡面"
eatnum=9
eated=0
while [ "${eated}" -lt "3" ]; do
check=$(( ${RANDOM} * ${eatnum} /32767+1 ))
mycheck=0
if [ "${eated}" -ge "1" ]; then
for i in $(seq 1 ${eated})
do
if [ ${eatedcon[$i]} == $check ]; then
mycheck=1
fi
done
fi
if [ "${mycheck}" == "0" ]; then
echo "你可以吃${eat[${check}]}"
eated=$((${eated} + 1 ))
eatedcon[${eated}]=${check}
fi
done
代码解释
这段脚本用于随机选择三种食物,输出供用户选择。脚本中的变量和数组含义如下:
-
PATH:环境变量,指定可执行文件的搜索路径。
-
eat:数组,包含九种食物。
-
eatnum:变量,表示数组元素个数。
-
eated:变量,表示已经选择的食物数量,初始值为0。
-
check:变量,用于存储随机选择的食物在数组中的索引。
-
mycheck:变量,用于判断已经选择的食物中是否已经包含了当前选中的食物。
-
eatedcon:数组,用于存储已经选择的食物在数组中的索引。
while循环中的逻辑如下:
-
当已经选择的食物数量小于3时,进行循环。
-
生成一个随机数check,表示在数组中的索引。
-
判断当前选中的食物是否已经被选择过,如果是则跳过,否则输出当前选中的食物,并将eated加1,同时将选中的食物在数组中的索引存入eatedcon数组中。
执行结果
[root@k8s-master-node1 shelldir]# sh noon_what_eat.sh
你可以吃奥里给
你可以吃平菇炒香干
你可以吃糖醋排骨
shell脚本的跟踪与调试
脚本文件在执行之前,最怕就是出现语法错误的问题。那么我们如何调试呢?有没有办法不需要通过直接执行脚本文件就可以判断是否有问题?我们直接用bash的相关参数
sh [-nvx] scripts.sh
选项与参数:
-n:不要执行脚本,仅查询语法的问题
-v:再执行脚本前,先将脚本文件的内容输出到屏幕上
-x:将使用到的脚本内容显示到屏幕上
使用案例
测试dir_perm.sh 有无语法问题
[root@k8s-master-node1 shelldir]# sh -n dir_rwx.sh
# 若没有语法问题,则不会显示任何信息
将noon_what_eat.sh 的执行过程全部列出来
[root@k8s-master-node1 shelldir]# sh -x noon_what_eat.sh
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:/root/shelldir
+ export PATH
+ eat[1]=红烧肉
+ eat[2]=糖醋排骨
+ eat[3]=小炒牛肉
+ eat[4]=小炒五花肉
+ eat[5]=平菇炒香干
+ eat[6]=香菇炒芹菜
+ eat[7]=喝西北风
+ eat[8]=奥里给
+ eat[9]=泡面
+ eatnum=9
+ eated=0
+ '[' 0 -lt 3 ']'
+ check=2
.....
.......