第四章 流程控制之条件判断
条件判断语句是一种最简单的流程控制语句。该语句使得程序根据不同的条件来执行不同的程序分支。本节将介绍Shell程序设计中的简单的条件判断语句。
if语句语法
单分支结构
# 语法1:
if <条件表达式>
then
指令
fi
#语法2:
if <条件表达式>;then
指令
fi
双分支结构
if <条件表达式>
then
指令序列1
else
指令序列2
fi
if id username;then
echo ""
else
echo " cunzai"
fi
多分支结构
语法:
if 条件表达式1;then
命令序列1
elif 条件表达式2;then
命令序列2
....
else
命令序列n
fi
案例
-
例1:编写脚本choice1.sh,利用单分支结构实现输入2个整数,输出最大值
[root@server ~]# vim choice1.sh
#!/bin/bash
read -p "请输入第一个整数: " x
read -p "请输入第二个整数: " y
max=$x
if (($max<y))
then
max=$y
fi
echo "最大值:$max"
-
例2:面试题,编写脚本choice2.sh,判断系统内存剩余容量大小,若低于100M则发送消息进行告警
#!/bin/bash
free_mem=$( free -m | grep Mem | tr -s " " | cut -d " " -f4 )
if (($free_mem<100))
then
echo "警告,剩余内存:$free_mem ,低于警戒线100MB"
else
echo "剩余内存:$free_mem,空间足够."
fi
# free -m :表示显示内存即虚拟内存的信息,-m以MB单位显示数字
# grep Mem:表示过滤包含Mem所在行
# tr -s " ": 压缩空格为一个
# cut -d " " -f4 :以空格为间隔符,取其第4部分
-
扩展:编写脚本,判断当前系统剩余内存大小,如果低于100M,邮件报警管理员,使用计划任务,每10分钟检查一次。
-
[root@locaklhost ~]# echo "邮件正文" | mail -s "邮件主题" alice
分析:核心是如何获取当前剩余内存大小
[root@localhost ~]# free -m | grep "Mem:" |tr -s " " | cut -d" " -f4
600
[root@localhost ~]# free -m | awk '/Mem:/ {print $4}'
600
vim mem.sh
mem=free -m |grep Mem | tr -s " " | cut -d “ ” -f 4
free -m | grep Mem | awk -F " " ‘{print }’
if test $mem \< 100 ;then
echo 内存严重不足 | mail -s 内存报警 lxx@163.com
else
echo 内存足够
fi
-
例3:编写脚本choice3.sh,判断当前脚本的执行者,若不是root账户则提示,并退出
# 检查当前账户的4种方法
[root@server ~]# whoami
root
#使用环境变量
[root@server ~]# echo $USER
root
#id -u,结果为零则为root
[root@server ~]# id -u
0
[root@server ~]# echo $UID
0
[root@server ~]# vim choice3.sh
#!/bin/bash
if [ "$USER" != "root" ] #或者if [ !$c_user == root ];then
then
echo "please switch user root"
fi
[root@server ~]# bash choice3.sh # root账户下执行
[root@server ~]# pwd
/root
[root@server ~]# mv choice3.sh / # 让普通账户能访问脚本,需要移动到/
[root@server ~]# chmod +x /choice3.sh # 赋予执行权限
[root@server ~]# ll /choice3.sh
-rwxr-xr-x 1 root root 81 6月 18 10:10 /choice3.sh
[root@server ~]# tail -1 /etc/passwd # 查看普通账户名
fox:x:1000:1000::/home/fox:/bin/bash
[root@server ~]# su fox # 切换账户
[fox@server root]$ cd /
[fox@server /]$ bash choice3.sh
please switch user root
-
例4:编写脚本choice4.sh,实现闰年的判断
[root@server ~]# vim choice4.sh
#!/bin/bash
read -p "请输入4位数整数年份: " year
if [ -n "$year" ] # 非空测试
then
if ((year%4==0)) && ((year%100!=0)) || ((year%400==0))
then
echo "闰年"
else
echo "平年"
fi
else
echo "请输入有效4位整数年份"
fi
-
例5:判断数字大于500则执行big.sh 小于等于500则退出脚本,并输出报错信息
read -p "s数字" num
if [ $num -gt 500 ] ;then
bash /big.sh
else
exit 0
fi
-
例6:编写脚本choice5.sh,判断sshd是否运行
分析:核心在于判断进程是否运行的方法?
方法1:查看进程 systemctl is-active sshd systemctl status sshd
[root@localhost ~]# ps -elf | grep sshd | grep -v grep | wc -l
4
方法2:查看端口
本地查看netstat、ss、lsof --- https://www.jianshu.com/p/644e75af4405
远程查看telnet、nmap、nc
[root@localhost ~]# netstat -lnupt | grep 22 | wc -l
2
[root@localhost ~]# ss -lnupt | grep 22 | wc -l
2
[root@localhost ~]# lsof -i:22
[root@localhost ~]# nmap -p 22 127.0.0.1 | grep open | wc -l
1
# 分析
# 根据进程数判断
[root@server ~]# ps -ef | grep sshd | grep -v grep | wc -l
# 根据端口号判断
[root@server ~]# netstat -lntup | grep 22 | wc -l
[root@server ~]# vim choice5.sh
#!/bin/bash
num=$(ps -ef | grep sshd | grep -v grep | wc -l)
if (($num>0))
then
echo "sshd is running"
else
echo "sshd is not running"
fi
方法2:
read -p "请输入要判断的程序名称:" server_name
count=`ps -aux | grep -cw $server_name`
if [ $count -gt 1 ];then
echo $server_name服务已运行
else
echo $server_name服务未运行
fi
-
例7:面试题,编写脚本choice6.sh,判断主机是否存活
[root@server ~]# vim choice6.sh
#!/bin/bash
read -p "请输入测试主机的IP地址:" ip
ping -c 2 -w 3 $ip &> /dev/null
# -c 2 表示发送2个数据包,-w 3 表示等待3秒结束,注意:不能等待1秒结束,有可能第二个数据包没有ping完就结束了,会返回错误的状态码
if [ $? -eq 0 ]
then
echo "主机$ip 已运行"
else
echo "主机$ip 未运行"
fi
# 上例修改,使用循环测试多台主机是否存活,设置显示颜色
[root@server ~]# vim choice6.sh
#!/bin/bash
for ip in 192.168.48.{125..135}
do
ping -c 2 -w 3 $ip &> /dev/null
if [ $? -eq 0 ]
then
echo -e "\e[1;31m!!主机 $ip 已运行!!\e[0m" # 高亮、红色
else
echo "主机$ip 未运行"
fi
done
-
例8:编写脚本choice7.sh,输入百分制成绩,判断等级成绩
[root@server ~]# vim choice7.sh
#!/bin/bash
read -p "请输入百分制成绩: " score
if [ -z $score ]
then
echo "未输入,请重新输入"
elif [ $score -lt 0 -o $score -gt 100 ]
then
echo "成绩输入有误,请输入0-100间数字"
elif [ $score -ge 90 ]
then
echo "成绩优秀"
elif [ $score -ge 80 ]
then
echo "成绩良好"
elif [ $score -ge 60 ]
then
echo "成绩及格"
else
echo "补考"
fi
- 扩展:根据用户输入成绩,判断优良中差。85-100 优秀--A 70-84 良好--B 60-69 合格--C 60分以下不合格--D
read -p "Please enter your score (0-100): " grade
if [ $grade -ge 85 ]
then
echo "$grade, A"
elif [ $grade -ge 70 ]
then
echo "$grade, B"
elif [ $grade -ge 60 ]
then
echo "$grade, C"
else
echo "$grade, D"
fi
更完善的:(多层嵌套)
grade=$1
if [[ $1 =~ ^[0-9]*$ ]];then #判断是否是纯数值,有小数也不行
if [ $grade -ge 85 -a $grade -le 100 ];then
echo "$grade, A"
elif [ $grade -ge 70 -a $grade -le 84 ];then
echo "$grade, B"
elif [ $grade -ge 60 -a $grade -le 69 ];then
echo "$grade, C"
elif [ $grade -ge 0 -a $grade -le 60 ];then
echo "$grade, D"
else
echo "输入的成绩不在(0-100)范围内"
fi
else
echo "请输入正确成绩"
fi
解释:$1 =~ ^[0-9]*$
=~:正则符匹配
^[0-9]:以0-9开头的任意一个
*:匹配前一个字符任意次(正则符)
$:结束符(保证数值结束前后没有其他信息)
-
例9:编写脚本,根据用户输入的内容,判断是数字、字母、其它字符
[root@server ~]# vim choice8.sh
#!/bin/bash
read -p "请输入数字、字母、符号: " str
if echo $str | grep [a-zA-Z] > /dev/null
then
echo "字母"
elif echo $str | grep [0-9] > /dev/null
then
echo "数字"
else
echo "符号"
fi
-
例10:编写脚本choice9.sh,判断当前主机cpu生产商
其信息在/proc/cpuinfo文件中vendor_id一行中。 如果其生产商为GenuineIntel,就显示其为Intel公司; 如果其生产商为AuthenticAMD,就显示其为AMD公司; 否则,就显示无法识别;
[root@server ~]# vim choice9.sh
#!/bin/bash
vendor=$(grep "vendor_id" /proc/cpuinfo | uniq | cut -d " " -f2)
if [ $vendor == GenuineIntel ]
then
echo "Intel"
elif [ $vendor == AuthenticAMD ]
then
echo "AMD"
else
echo "other"
fi
- 例11:两个整数比较大小(if嵌套)
read -p "请输入整数" a
read -p "在次输入" b
if [ -n "$a" -a -n "$b" ];then
[[ "$a" =~ ^[0-9]+$ ]] && [[ "$b" =~ ^[0-9]+$ ]]
# echo "$a"|[ -n "`sed -n '/^[0-9]*$/p'`" ] && echo "$b" |[-n "`sed -n '/^[0-9]*$/p'`"]
if [ $? -eq 0 ];then
if [[ "$a" -gt "$b" ]];then
echo 第一个数大于第二个数
elif [[ "$a" -lt "$b" ]];then
echo 第一个数小于第二个数
else
echo 两个数相等
fi
else
echo 请输入数字
fi
else
echo 输入信息不能为空
fi
exit退出程序 (复习)
- exit语句的基本作用是终止Shell程序的执行。
- 除此之外,exit语句还可以带一个可选的参数,用来指定程序退出时的状态码。
- exit语句的基本语法如下: exit status
- 其中,status参数表示退出状态,该参数是一个整数值,其取值范围为0~255。
- 与其他的Shell命令的一样,Shell程序的退出状态也储存在系统变量$?中,因此,用户可以通过该变量取得Shell程序返回给父进程的退出状态码。
case多条件判断
case可针对谋变量不同的值结果进行情景分析。
格式
case 变量名 in
值1)
指令1
;;
值2)
指令2
;;
值3)
指令3
;;
*)
默认
esac
也可以写在一行,例: 值1) 指令1 ;;
最后一个;;可以写也可以不写
执行过程
-
case语句会将该变量的值与 )括号中的值相比较,如果与某个值相等,则执行对应语句。
-
当遇到“;;”符号时,就跳出case语句,执行esac语句后面的语句。
-
如果没有与任何一个值相匹配,则执行*)后面的一组语句
示例
-
例1:编写脚本choice10.sh,对上例7的百分制成绩判断等级成绩进行改写
[root@server ~]# vim choice10.sh
#!/bin/bash
read -p "请输入百分制成绩: " score
case $score in
9[0-9]|100)
echo "成绩优秀"
;;
8[0-9])
echo "成绩良好"
;;
6[0-9]|7[0-9])
echo "成绩及格"
;;
*)
echo "补考"
esac
-
例2:判断今天是星期几,根据星期几来判断今天是工作日还是休息日
[root@server ~]# vim choice11.sh
#!/bin/bash
a=`date +%w` #将日期转换为星期几,用数字0-6表示
case $a in
[1-5])
echo "今天是工作日,社畜没有休息日哦。";;
[06])
echo "哦~今天是休息日呢,请好好休息哦~";;
esac
- 例3:由用户从键盘输入一个字符,并判断该字符是否为字母、数字或者其他字符, 并输出相应的提示信息
read -p "Please enter a character, press enter to continue: " -n 1 str
if echo "$str"|grep "[a-zA-Z]" >/dev/null
then
echo "Input is letter"
elif echo "$str"|grep "[0-9]" >/dev/null
then
echo "Input is number"
else
echo "Input is other"
fi
read -p "Please enter a character, press enter to continue: " -n 1 KEY
case "$KEY" in
[[:alpha:]])
echo "Input is letter"
;;
[0-9])
echo "Input is number"
;;
*)
echo "Input is other characters"
esac
- 例4:将判断分数范围多分支语句用case语句实现
read -p "Please enter your score (0-100): " grade
case "$grade" in
8[5-9]|9[0-9]|100) #{85..100} [85-100]
echo "$grade, A"
;;
7[0-9]|8[0-4])
echo "$grade, B"
;;
6[0-9])
echo "$grade, C"
;;
*)
echo "$grade, D"
esac
练习
-
开发rsync起停脚本
[root@server ~]# vim cc_rsync
#!/bin/bash
if [ "$#" -ne 1 ]; then
echo "Usage: $0 {start|stop|restart}"
exit 1
fi
# 当用户选择是start
case "$1" in
"start")
/usr/bin/rsync --daemon
sleep 1
if [ `netstat -lntup | grep rsync | wc -l` -ge 1 ]; then
echo "rsync is started"
exit 0
fi
;;
"stop")
killall rsync &>/dev/null
sleep 1
if [ `netstat -lntup | grep rsync | wc -l` -eq 0 ]; then
echo "rsync is stopped"
exit 0
fi
;;
"restart")
if [ `netstat -lntup | grep rsync | wc -l` -ge 1 ]; then
killall rsync &>/dev/null
sleep 1
fi
/usr/bin/rsync --daemon
sleep 1
start_flag=`netstat -lntup | grep rsync | wc -l`
#if [ "$stop_flag" -eq 0 -a "$start_flag" -ge 1 ]; then
#if [ "$stop_flag" -eq 0 && "$start_flag" -ge 1 ]; then
if [ `netstat -lntup | grep rsync | wc -l` -ge 1 ]; then
echo "rsync is restarted"
exit 0
fi
;;
*)
echo "Usage: $0 {start|stop|restart}"
esac
扩展远程端口监听 telnet nmap nc
-
nc:netcat 是一个简单的 Unix 工具,它使用 TCP 或 UDP 协议去读写网络连接间的数据。
-
nmap
:(“Network Mapper”)是一个用于网络探索和安全审计的开源工具,被设计用来快速地扫描大规模网络。 -
telnet
:被用来交互地通过 TELNET 协议与另一台主机通信。
1)nc
即 netcat
。netcat
是一个简单的 Unix 工具,它使用 TCP 或 UDP 协议去读写网络连接间的数据。
它被设计成为一个可信赖的后端工具,可被直接使用或者简单地被其他程序或脚本调用。
与此同时,它也是一个富含功能的网络调试和探索工具,因为它可以创建你所需的几乎所有类型的连接,并且还拥有几个内置的有趣功能。
netcat
有三类功能模式,它们分别为连接模式、监听模式和隧道模式。
nc
(netcat
)命令的一般语法:
$ nc [-options] [HostName or IP] [PortNumber]
在下面的例子中,我们将检查远程 Linux 系统中的 22 端口是否开启。
假如端口是开启的,你将获得类似下面的输出。
# nc -zvw3 192.168.1.8 22
Connection to 192.168.1.8 22 port [tcp/ssh] succeeded!
命令详解:
-
nc
:即执行的命令主体; -
z
:零 I/O 模式(被用来扫描); -
v
:显式地输出; -
w3
:设置超时时间为 3 秒; -
192.168.1.8
:目标系统的 IP 地址; -
22
:需要验证的端口。
当检测到端口没有开启,你将获得如下输出:
# nc -zvw3 192.168.1.95 22
nc: connect to 192.168.1.95 port 22 (tcp) failed: Connection refused
2)nmap
(“Network Mapper”)是一个用于网络探索和安全审计的开源工具,被设计用来快速地扫描大规模网络,尽管对于单个主机它也同样能够正常工作。
nmap
以一种新颖的方式,使用裸 IP 包来决定网络中的主机是否可达,这些主机正提供什么服务(应用名和版本号),它们运行的操作系统(系统的版本),它们正在使用的是什么包过滤软件或者防火墙,以及其他额外的特性。
尽管 nmap
通常被用于安全审计,许多系统和网络管理员发现在一些日常任务(例如罗列网络资产、管理服务升级的计划、监视主机或者服务是否正常运行)中,它也同样十分有用。
nmap
的一般语法:
$ nmap [-options] [HostName or IP] [-p] [PortNumber]
假如端口是开启的,你将获得如下的输出:
# nmap 192.168.1.8 -p 22
Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-16 03:37 IST Nmap scan report for 192.168.1.8 Host is up (0.00031s latency).
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 13.06 seconds
假如端口没有开启,你将得到类似下面的结果:
# nmap 192.168.1.8 -p 80
Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-16 04:30 IST
Nmap scan report for 192.168.1.8
Host is up (0.00036s latency).
PORT STATE SERVICE
80/tcp closed http
Nmap done: 1 IP address (1 host up) scanned in 13.07 seconds
3)telnet
命令被用来交互地通过 TELNET 协议与另一台主机通信。
telnet
命令的一般语法:
$ telnet [HostName or IP] 80
假如探测成功,你将看到类似下面的输出:
$ telnet 192.168.1.9 22
Trying 192.168.1.9...
Connected to 192.168.1.9.
Escape character is '^]'.
SSH-2.0-OpenSSH_5.3
^]
Connection closed by foreign host.
假如探测失败,你将看到类似下面的输出:
$ telnet 192.168.1.9 80
Trying 192.168.1.9...
telnet: Unable to connect to remote host: Connection refused
示例 2:检查主机是否存活,并输出结果
分析:最简单方式是通过ping命令检测
ping -c 2 -W 1 192.168.95.16 &> /dev/null
通过$?判断,也可以直接 if ping -c 2 -W 1 192.168.95.16 &> /dev/null测试
扩展:
[root@bogon day04]# dnf whatprovides nmap #查询命令nmap所能提供的安装包