[LN_11] Shell编程-条件判断&流程控制语句(
目录结构
一、条件判断式语句
1. 按文件类型判断
2. 按文件权限判断
3. 两文件比较
4. 两整数比较
5. 字符串的判断
6. 多重条件判断
二、单分支if语句
1. 单分支if语句-语法结构
2. 案例:判断当前登录用户是否是root
3. 案例:判断分区使用率
三、双分支if语句
1. 案例:判断输入的是否是一个目录
2. 案例:判断Apache服务是否启动
四、多分支if语句
1. 多分支if语句-语法结构
2. 案例:计算器
3. 案例:判断用户输入的是什么文件
五、多分支case条件语句
六、for循环
1. for循环
1)批量压缩&解压-案例
2)从1开始的连续整数加和-案例
2. 案例:批量添加&删除指定数量的用户
七、while循环 & until循环
1. while循环
2. until循环
一、条件判断式语句
1. 按文件类型判断
测试选项 | 作用 |
---|---|
-b 文件 | 判断该文件:是否存在,且是否为块设备文件 (符合条件则为True) |
-c 文件 | 判断该文件:是否存在,且是否为字符设备文件 |
-d 文件 |
判断该文件:是否存在,且是否为目录文件 |
-e 文件 |
判断该文件:是否存在 |
-f 文件 |
判断该文件:是否存在,且是否为普通文件 |
-L 文件 | 判断该文件:是否存在,且是否为符号链接文件 |
-p 文件 | 判断该文件:是否存在,且是否为管道文件 |
-s 文件 | 判断该文件:是否存在,且是否为非空 |
-S 文件 | 判断该文件:是否存在,且是否为套接字文件 |
两种判断格式:
格式1:test -e /root/install.log
格式2:[ -e /root/install.log ]
PS:格式2中中括号两内侧需要连接空格
# 第1个判断目录若正确执行,则打印"yes",否则打印"no"
[ -d /root ] && echo "yes" || echo "no"
[ -e /root ] && echo "yes" || echo "no"
[ -f /root ] && echo "yes" || echo "no"
test -e /root/install.log && echo "yes" || echo "no"
![](https://img.haomeiwen.com/i4866277/7763a314feb66da2.png)
2. 按文件权限判断
测试选项 | 作用 |
---|---|
-r 文件 |
判断该文件:是否存在,且是否拥有读 权限 |
-w 文件 |
判断该文件:是否存在,且是否拥有写 权限 |
-x 文件 |
判断该文件:是否存在,且是否拥有执行 权限 |
-u 文件 | 判断该文件:是否存在,且是否拥有SUID权限 |
-g 文件 | 判断该文件:是否存在,且是否拥有SGID权限 |
-k 文件 | 判断该文件:是否存在,且是否拥有SBit权限 |
通过执行者判断权限(rwx):所有者-所属组-其他人
如:ll 文件名
![](https://img.haomeiwen.com/i4866277/93107acca02c7caf.png)
3. 两文件比较
测试选项 | 作用 |
---|---|
文件1 -nt 文件2 | 判断修改时间新旧:是否文件1最新 |
文件1 -ot 文件2 | 判断修改时间新旧:是否文件1最旧 |
文件1 -ef 文件2 | 判断两个文件的Inode编号是否一致(是否同一个文件) 可用于判断硬链接 |
nt:new time
ot:old time
ef:equal file 是否为相同的源文件
# 创建硬链接
ln /tmp/test007/sh_Test/student.txt /tmp/test007/stu.txt
# 切换目录
cd /tmp/test007
# 判断是否同源的链接
[ ./stu.txt -ef ./sh_Test/student.txt ] && echo "yes" || echo "no"
# 查看Inode编号
ll -i ./stu.txt ./sh_Test/student.txt
![](https://img.haomeiwen.com/i4866277/6baa54ce7cc33472.png)
4. 两整数比较
前提:A、B均为正数
测试选项 | 作用 |
---|---|
A -eq B | 判断:是否 [A == B] |
A -ne B | 判断:是否 [A != B] |
A -gt B | 判断:是否 [A > B] |
A -lt B | 判断:是否 [A < B] |
A -ge B | 判断:是否 [A >= B] |
A -le B | 判断:是否 [A <= B] |
eq: equal
ne: not equal
gt: greater than
lt: less than
ge: greater or equal
le: less or equal
PS:Shell默认是字符串类型,但若加入了数值比较符号,则Shell会自动将比较的数值转为整型进行比较
[ 25 -ge 22 ] && echo "yes" || echo "no"
[ 25 -gt 22 ] && echo "yes" || echo "no"
[ 25 -ne 22 ] && echo "yes" || echo "no"
[ 25 -lt 22 ] && echo "yes" || echo "no"
![](https://img.haomeiwen.com/i4866277/72b4d6ba54daaee9.png)
5. 字符串的判断
测试选项 | 作用 |
---|---|
-z 字符串 | 判断字符串:是否为空 |
-n 字符串 | 判断字符串:是否非空 |
字串1 == 字串2 | 判断字符串:是否 [字串1 == 字串2] |
字串1 != 字串2 | 判断字符串:是否 [字串1 != 字串2] |
PS:[ ! -z 字符串 ] 等效于 [ -n 字符串 ]
# 为name变量赋值
name=Jack
# 判断name变量是否为空
[ -z "$name" ] && echo "yes" || echo "no"
![](https://img.haomeiwen.com/i4866277/d87b11f6bc4edbe3.png)
# 赋值
x=11 y=22
# 从字符串角度判断:x和y是否相等
[ "$x" == "$y" ] && echo "yes" || echo "no"
# 从整数角度判断:x和y是否相等
[ "$x" -eq "$y" ] && echo "yes" || echo "no"
![](https://img.haomeiwen.com/i4866277/5b3ea275798b7cc9.png)
6. 多重条件判断
测试选项 | 作用 |
---|---|
判断1 -a 判断2 | 逻辑与。所有判断都成立,执行结果才为真 |
判断1 -o 判断2 | 逻辑或。只要有任意一个判断成立,执行结果即为真 |
! 判断 | 逻辑非。对原始的判断式取反 |
a:and
o:or
# 判断变量x是否非空,同时判断x是否大于20
x=22
[ -n "$x" -a "$x" -gt 20 ] && echo "yes" || echo "no"
![](https://img.haomeiwen.com/i4866277/1861ae29ac9329c4.png)
二、单分支if语句
1. 单分支if语句-语法结构
语法结构:
# 风格1:
if [ 条件判断式 ];then
程序主体
fi
# 风格2:
if [ 条件判断式 ]
then
程序主体
fi
语法说明:
① if语句使用fi
结尾,和一般语言使用大括号结尾不同
② [ 条件判断式 ]
即test命令判断,中括号和条件判断式之间必须留有空格
③ then
后跟满足条件的执行程序,then可放在[]之后,用";"分割;也可换一行写入then,此时不需要";"
2. 案例:判断当前登录用户是否是root
思路分析:
Step1-利用命令env
可查看到 "USER=root"的一行内容,该字段的值即为当前登录的用户名称
Step2-提取USER字段值的过程:先用grep提取该行数据,然后用cut提取该行的第2列数据(分隔符为"="),即为USER字段的值
Step3-将提取的值赋给变量,再对变量与预期的名称root作比较,判断是否相等
![](https://img.haomeiwen.com/i4866277/438ac838e2f7c3ef.png)
测试命令:
# 查看环境变量
env
# 提取当前登录的用户
env | grep "USER" | cut -d "=" -f 2
![](https://img.haomeiwen.com/i4866277/fdab1838ee8447c5.png)
程序实现:
vim if-001.sh
#!/bin/bash
test=$(env | grep "USER" | cut -d "=" -f 2)
if [ "$test" == "root" ]
then
echo "当前用户是root"
fi
配置权限:chmod 755 if-001.sh
执行脚本:./if-001.sh
![](https://img.haomeiwen.com/i4866277/cd4727fef4cac782.png)
3. 案例:判断分区使用率
提取挂载在根分区/
的文件系统所用的容量占比是否超过90%
![](https://img.haomeiwen.com/i4866277/d70bace4877e377a.png)
分析:
# 提取挂载点在根目录的一行数据(以下利用正则匹配)
df -h | grep "/$"
# 提取该行数据中 Use%字段值,对应位置变量:$4(具体根据不同系统而相异)
df -h | grep "/$" | awk '{print $4}'
# 对以上提取值处理:以 "%"为分隔符提取第 1列数据
df -h | grep "/$" | awk '{print $4}' | cut -d "%" -f 1
测试命令:
![](https://img.haomeiwen.com/i4866277/dc6742e7dbbe39e6.png)
程序实现:
vim if-002.sh
#!/bin/bash
test=$(df -h | grep "/$" | awk '{print $4}' | cut -d "%" -f 1)
if [ "$test" -ge 90 ]
then
echo "根分区已接近满容量!"
fi
![](https://img.haomeiwen.com/i4866277/4f8a7918cc266b14.png)
三、双分支if语句
1. 案例:判断输入的是否是一个目录
语法结构:
if [ 条件判断式 ]
then
执行程序1
else
执行程序2
fi
程序实现:
vim if-003.sh
#!/bin/bash
# 将用户输入的信息保存到变量dir中
read -t 30 -p "Please input a dir: " dir
if [ -d "$dir" ]
then
echo "Yes, it's a directory"
else
echo "No, it isn't a directory"
fi
![](https://img.haomeiwen.com/i4866277/b26f762ac60efe1d.png)
2. 案例:判断Apache服务是否启动
# 查看系统中正在运行的进程
ps aux
# 过滤grep进程的干扰,筛选出只包含Apache的进程(httpd)
ps aux | grep "httpd" | grep -v "grep"
![](https://img.haomeiwen.com/i4866277/c3aa7b0e87a58911.png)
程序实现:
vim if-004.sh
#!/bin/bash
# 截取httpd进程,并把结果赋给变量test
test=$(ps aux | grep "httpd" | grep -v "grep")
# 若 test值非空,则执行 then中的命令,否则执行 else中的命令
if [ -n "$test" ]
then
# 输出正确重定向:将命令执行结果保存到指定文件中(追加到原内容之后)
echo "$(date): httpd is OK" >> /tmp/test007/sh_Test/log/auto-acc.log
else
# 同时保存正确和错误输出的结果到指定文件中(覆盖)
# 启动 httpd的命令根据各自配置而定(本次已配置过 phpstudy for Linux环境)
phpstudy start &>/dev/null
# 将命令执行结果保存到指定文件中(追加)
echo "$(date): restart httpd !" >> /tmp/test007/sh_Test/log/auto-err.log
fi
chmod 755 if-004.sh
./if-004.sh
![](https://img.haomeiwen.com/i4866277/90ba0cbc3601811d.png)
以上程序实现的效果,相当于是检测服务器上的Apache进程是否开启,若未开启则启动它,保持服务器运行httpd服务。可将该脚本部署在自动控制执行脚本的服务上,在指定的时间执行指定的脚本。
四、多分支if语句
1. 多分支if语句-语法结构
语法结构:
if [ 条件判断式1 ]
then
执行程序1
elif [ 条件判断式2 ]
then
执行程序2
# 省略更多条件......
else
执行程序n
fi
2. 案例:计算器
案例目标:
输入2个正整数,输入运算符,进行加减乘除运算
初步分析:
判断是否有输入(非空、空)
判断输入内容是否是数值(数字、非数字、空)
判断输入的运算符(匹配时进行相应格式计算)
执行运算符操作(加减乘除),输出结果
异常处理:
加减乘除:包含空、非数字时,不能参与运算,执行结果抛出异常提示
除法:除数不能为0,为0时抛出异常提示
程序实现:
vim if-005.sh
#!/bin/bash
# 将输入值or符号赋给对应的变量
read -p "请输入第1个数字:" num1
read -p "请输入第2个数字:" num2
read -p "请输入运算符[+-*/]:" opt
# 判断以上3个输入,若有任何一个输入为空,则执行then并退出程序
# 相当于是非空验证,只有3个输入都非空时才会执行后续语句
if [ -z "$num1" -o -z "$num2" -o -z "$opt" ]
then
echo "输入异常:不能包含空字符!"
exit 1
# 判断3个输入都非空时,执行后续语句
elif [ -n "$num1" -a -n "$num2" -a -n "$opt" ]
then
# 将输入的字符中包含数字的部分全部替换为空
# 若替换后的结果为空,则说明输入的是数字,否则为非数字
test_1=$(echo $num1 | sed 's/[0-9]//g')
test_2=$(echo $num2 | sed 's/[0-9]//g')
# 对以上替换后的结果进行判断:2个结果中只要有任意一个是非空,则执行then并退出程序
# 2个结果都为空时(输入的为数字),才会执行后续的elif
if [ -n "$test_1" -o -n "$test_2" ]
then
echo "输入异常:请检查数字、运算符[+-*/]是否正确!"
exit 2
elif [ "$opt" == "+" ] # 加法计算公式
then
result=$(($num1+$num2))
elif [ "$opt" == "-" ] # 减法计算公式
then
result=$(($num1-$num2))
elif [ "$opt" == "*" ] # 乘法计算公式
then
result=$(($num1*$num2))
elif [ "$opt" == "/" ] # 除法计算公式
then
# 除法时,对除数是否为零验证
if [ "$num2" -eq 0 ]
then
echo "输入异常:除数不能为零!"
exit 3
else
result=$(($num1/$num2))
fi
else # 对以上4种运算符号[+-*/]以外的其他符号输入,抛出提示并退出程序
echo "输入异常:请检查所输入数字、运算符[+-*/]的格式是否正确!"
exit 4
fi
fi
# 引用变量,输出[+-*/]通用的计算表达式
echo "$num1 $opt $num2 = $result"
PS:
①判断字符串是否相等(如:"$opt" == "+"
),其中==
左右两侧需要留有空格,否则执行可能报错
②以上程序中若使用test_opt=$(echo $opt | sed 's/[+*/-]//g')
进行替换输入的加减乘除符号,对于乘号(*)暂时没有找到座位普通字符来处理的方案,默认会作为通配符;减号(-)在中括号[]中最好放在末尾位置,若放在其他位置可能引起系统执行歧义,如:范围的连接符
测试用例:
用例编号 | 输入数字1 | 输入数字2 | 输入运算符 | 预期输出 |
---|---|---|---|---|
001 | 9 | 7 | + | 16 |
002 | 9 | 7 | - | 2 |
003 | 9 | 7 | * | 63 |
004 | 9 | 7 | / | 1 |
005 | 9 | (空) | + | "输入异常:不能包含空字符!" |
006 | 9 | 7 | (空) | "输入异常:不能包含空字符!" |
007 | 9a | 7 | - | 输入异常:请检查数字、运算符[+-*/]是否正确! |
008 | 9 | 0 | / | 输入异常:除数不能为零! |
009 | 9 | 7 | abc | 输入异常:请检查所输入数字、运算符[+-*/]的格式是否正确! |
010 | 9 | 7 | ** | 输入异常:请检查所输入数字、运算符[+-*/]的格式是否正确! |
授权&执行:
chmod 755 if-005.sh
./if-005.sh
执行结果:
![](https://img.haomeiwen.com/i4866277/3ee893f8a009ff51.png)
![](https://img.haomeiwen.com/i4866277/d154bf0c7e51c2de.png)
![](https://img.haomeiwen.com/i4866277/5b517d8068aa2ea7.png)
以上,实际执行结果与预期输出相匹配
3. 案例:判断用户输入的是什么文件
初步分析:
判断输入是否非空
判断是否是系统存在的文件or目录
判断是否为普通文件
判断是否为目录文件
异常处理:
输入为空、不存在的文件or目录、普通文件or目录文件以外的文件
程序实现:
vim if-006.sh
#!/bin/bash
# 接收键盘输入,赋值给变量file
read -p "请输入一个目录名:" file
# 判断file变量是否为空,为空时抛出异常提示并退出程序
# file非空时,继续执行后续程序
if [ -z "$file" ]
then
echo "输入异常:不能为空!"
exit 1
# 判断文件是否存在
elif [ ! -e "$file" ]
then
echo "输入异常:非系统存在的文件名称,请重新确认!"
exit 2
# 判断是否为普通文件
elif [ -f "$file" ]
then
echo "$file:普通文件"
# 判断是否为目录文件
elif [ -d "$file" ]
then
echo "$file:目录文件"
else
echo "$file:其他类型的文件"
fi
测试用例:
用例编号 | 输入 | 预期输出 |
---|---|---|
001 | /root | /root:目录文件 |
002 | hello.sh | hello.sh:普通文件 |
003 | /dev/sda1 | /dev/sda1:其他类型的文件 |
004 | (空) | 输入异常:不能为空! |
005 | abcdeaaa | 输入异常:非系统存在的文件名称,请重新确认! |
执行结果:
![](https://img.haomeiwen.com/i4866277/552a852b8d90322b.png)
五、多分支case条件语句
case语句与if...elif...else语句对比:
① 都是多分支条件语句
② case语句只能判断一种条件关系,而if语句可判断多种条件关系
语法结构:
case $变量名 in
"值1")
执行程序1
;;
"值2")
执行程序2
;;
# ......
*)
执行程序n
;;
esac
PS:
1)case语句以case
开头,esac
结尾
2)变量匹配到某个值,则执行对应程序;每个执行程序后一行都需要加上双分号;;
3)最后一个取值用 *
,不需要加双引号,此时表示以上所有取值都不满足时执行的程序
如:
vim case-001.sh
#!/bin/bash
echo "90-100,请输入 A"
echo "80-89,请输入 B"
echo "70-79,请输入 C"
echo "60-69,请输入 D"
read -p "请输入您的选择:" cho
case "$cho" in
"A")
echo "Score:90-100"
;;
"B")
echo "Score:80-89"
;;
"C")
echo "Score:70-79"
;;
"D")
echo "Score:60-69"
;;
*)
echo "Score is less than 60"
;;
esac
![](https://img.haomeiwen.com/i4866277/095cbebd2bc003f3.png)
六、for循环
1. for循环
语法结构:
语法1
# 每次循环会将值依次赋给变量,变量如:i
for 变量 in 值1 值2 值3...
do
执行程序
done
1)批量压缩&解压-案例
案例:先批量压缩文件为.tar.gz压缩包格式,然后再对其进行批量解压
Step1-批量压缩:
vim for-001.sh
#!/bin/bash
cd /tmp/test007/sh_Test
for i in $(ls if-*.sh)
do
# 压缩所有以"if-"开头的.sh文件为各自文件名命名的.tar.gz压缩文件
tar -zcvf "$i"-.tar.gz $i
done
# 将当期目录下的所有.tar.gz压缩包移动到./ysb目录下保存
mv *.tar.gz ./ysb
![](https://img.haomeiwen.com/i4866277/47ac8f2a33509581.png)
Step2-批量解压:
vim for-002.sh
#!/bin/bash
cd /tmp/test007/sh_Test/ysb
for i in $(ls *.tar.gz)
do
# 解压所有.tar.gz压缩包
tar -zxvf $i
done
# 将当期目录下的所有.tar.gz压缩包删除(保留解压文件)
rm -rf *.tar.gz
![](https://img.haomeiwen.com/i4866277/6c87556b4767ac18.png)
解压脚本也可写作如下方式:
vim for-003.sh
#!/bin/bash
cd /tmp/test007/sh_Test/ysb
ls *.tar.gz > ysb.log
for i in $(cat ysb.log)
do
# 解压所有.tar.gz压缩包,不显示详细的解压过程
tar -zxf $i &>/dev/null
done
# 将当期目录下的所有.tar.gz压缩包删除(保留解压文件)
# 同时删除保存压缩包文件名称的日志文件
rm -rf *.tar.gz ysb.log
![](https://img.haomeiwen.com/i4866277/903b0f48ef0366ba.png)
语法2
for((初始值;循环控制条件;变量变化))
do
执行程序
done
PS:
Shell脚本中,对于数值运算时,需要加双括号(())
2)从1开始的连续整数加和-案例
案例:1+2+3+...+100=?
vim for-004.sh
#!/bin/bash
s=0
for ((i=1;i<=100;i=i+1))
do
s=$(($s+$i))
done
echo "1+2+3+...+100 = $s"
![](https://img.haomeiwen.com/i4866277/7e66a45a0e0a0b31.png)
案例:1+2+3+...+N=?
对于正整数N,用户可自由输入不同的数值
vim for-005.sh
#!/bin/bash
echo "计算1+2+3+...+N=?"
read -p "请输入连续加和的最大整数 N:" num
s=0
for ((i=1;i<=$num;i=i+1))
do
s=$(($s+$i))
done
echo "1+2+3+...+$num = $s"
![](https://img.haomeiwen.com/i4866277/ef55b49832825adc.png)
2. 案例:批量添加&删除指定数量的用户
案例:批量添加指定数量的用户
vim for-006.sh
#!/bin/bash
# 根据提示进行输入
read -p "请输入用户名:" name
read -p "请输入添加的用户总数:" num
read -p "请输入用户的密码:" -s pass # 输入隐藏显示
# 以上输入的3个数据都非空时,才会执行后续程序
if [ -n "$name" -a -n "$num" -a "$pass" ]
then
# 验证 $num是否是数字
x=$(echo "$num" | sed 's/[0-9]//g')
if [ -z "$x" ]
then
# 循环添加指定数量的用户
for((i=1;i<=$num;i=i+1))
do
# 自增编号添加用户,添加过程不作输出显示
/usr/sbin/useradd "$name-$i" &>/dev/null
# 接收从标准输入的密码,赋予每个用户初始密码
echo "$pass" | /usr/bin/passwd --stdin "$name-$i"
done
fi
fi
执行结果:
![](https://img.haomeiwen.com/i4866277/74ed1210493849b5.png)
![](https://img.haomeiwen.com/i4866277/743dff33b4d3adc4.png)
![](https://img.haomeiwen.com/i4866277/ffd44a32be599c6c.png)
案例:批量删除指定的用户
先提取以上测试新增的用户名:
cat /etc/passwd | grep "/bin/bash" | grep -v -E "root|test001" | cut -d ":" -f 1
![](https://img.haomeiwen.com/i4866277/1b0dc4517c48a5f9.png)
vim for-007.sh
#!/bin/bash
# 筛选出测试新值的所有用户,赋给变量 user
user=$(cat /etc/passwd | grep "/bin/bash" | grep -v -E "root|test001" | cut -d ":" -f 1)
for i in $user
do
# 循环删除筛选出的用户(连带删除其家目录)
userdel -r $i
done
执行结果:
![](https://img.haomeiwen.com/i4866277/2bd7917c8a670b96.png)
七、while循环 & until循环
1. while循环
while循环是不定循环,也称条件循环。只要条件判断式成立,则循环就会一直继续,直到条件判断式不成立为止(应当避免永不停止的循环,即条件判断式不能固定为true不变)
PS:for循环是固定循环
语法结构:
while [ 条件判断式 ]
do
执行程序
done
案例:1+2+3+...+N=?
vim while-001.sh
#!/bin/bash
read -p "请输入连续加和的最大整数N:" num
i=1 result=0
while [ $i -le $num ]
do
result=$(($result+$i))
i=$(($i+1))
done
echo "连加到数字$num的结果为:$result"
执行结果:
![](https://img.haomeiwen.com/i4866277/3af982f5e5567398.png)
2. until循环
until循环和while循环相反,其只要条件判断式不成立则进行循环,一旦条件判断式成立时则终止循环
语法结构:
until [ 条件判断式 ]
do
执行程序
done
vim until-001.sh
#!/bin/bash
read -p "请输入连续加和的最大整数N:" num
i=1 result=0
until [ $i -gt $num ]
do
result=$(($result+$i))
i=$(($i+1))
done
echo "连加到数字$num的结果为:$result"
执行结果:
![](https://img.haomeiwen.com/i4866277/208d5bb70cd99341.png)
PS:
以上while循环和until循环,若可能的话,建议用for循环来替代,以降低误操作产生永不停止循环的风险