文章目录
- shell脚本的条件判断式
- 利用 if...then
- 单层、简单条件判断式
- 多重、复杂条件判断式
- 例题1
- 例题2
- 利用case...esac判断
- 例题1
- 利用function功能
- 例题1
shell脚本的条件判断式
很多时候我们必须要根据某些数来判断程序该如何举例来说,我们在之前的练习中让用户输入Y/N的时候,必须要执行不同的信息输入,可以使用&& 和||的方式,那么如果我们要执行一堆命令呢?真的要if then 来帮忙。
利用 if…then
这个if…then是最常见的条件判断式了。简单来说,当符合某个条件判断的时候,就允许他进行某项任务。这个if…then的判断还有多层次的情况。
单层、简单条件判断式
如果你只有一个判断式要进行。那么我们可以简单地这样看:
if [ 条件判断式 ]; then
当条件成立时,可以进行的命令工作内容
fi # 将if 反过来写,就成为fi,意思就是结束if
如果我们有多个条件判别的话,就像之前的案例所写的,就是将【多个条件写入一个中括号内的情况】,我们还可以有多个括号隔开。而括号和括号之间,则以&&或||隔开
- &&代表AND
- ||代表or
所以我们之前使用中括号判别到底输入的是不是Y/y,就可以这样修改
[ "${yn}" == "Y" -o "${yn}" == "y" ]
上面的案例可以替换为
[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]
那么我们再来用if…then的样式来看看:
上图是我们之前案例的脚本内容我们来做一下修改
[root@localhost shelldir]# vim continue.sh
#!/bin/bash
# 程序说明:
# 这个程序就是让用户做选择
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p "请输入(Y/N):" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo -e "\n您输入了Y/y,请继续"
exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo -e "\n您输入了N/n,请退出"
exit 0
fi
echo -e "没有识别到您输入了什么,重新输入\a" && exit 0
执行结果(输入Y看结果)
[root@localhost shelldir]# sh continue.sh
请输入(Y/N):Y
您输入了Y/y,请继续
执行结果(什么也没输入)
[root@localhost shelldir]# sh continue.sh
请输入(Y/N):
没有识别到您输入了什么,重新输入
你可能会问,上述案例写的这么复杂没有之前的代码简单,但是如果你以逻辑概念来看,其实上面的范例中,我们使用了两个条件判断,明明仅有一个${yn}的变量,为什么要进行两次比对呢?我们再来用多重条件判断式来试试
多重、复杂条件判断式
就像上述例子一样,如果该数据需要进行多种不同的判断,我只想执行一次${yn}的判断就好,不想做多次if的判断,那样应该怎么做呢?
一个条件判断,分成功执行与失败执行(else)
if [ 条件判断式 ]; then
当条件判断式成立时,可执行的命令
else
当条件判断式不成立时,可执行的命令
fi
如果你遇到非常复杂的判断情况,则可以使用这个语法
if [ 条件判断式 ]; then
当条件判断式成立时,可执行的命令
elif [ 条件判断式2 ]; then
当条件判断式2成立时,可执行的命令
else
当条件判断式1与2均不成立时,可执行的命令
fi
要注意的是,【elif】也是个判断式,因此【elif】后面都要接then来处理。else则是最后没有成立的结果,不用加then。我们可以将之前的案例改为
#!/bin/bash
# 程序说明:
# 这个程序就是让用户做选择
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p "请输入(Y/N):" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo -e "\n 您输入了Y/y,请继续"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo -e "\n 您输入了N/n,请退出"
else
echo -e "\n我不知道你输入的是什么,重新输入\a"
fi
程序是不是变得非常容易看懂了,可以避免重复判断的状况。
例题1
一般来说,如果你不希望用户键盘输入额外的数据,则可以使用【$1】参数功能,让用户执行命令时将参数带进去。现在我想让用户输入【hello】这个关键字时,利用参数的方法可以这样依序设计
- 判断$1是否为hello,如果是的话,就显示"你好,最近过的好吗?"
- 如果没有加任何参数,就提示用户必须要使用的参数执行法;
- 而如果加入的参数不是hello,就提醒用户仅能使用hello为参数。
[root@localhost shelldir]# vim howareyou.sh
# 程序说明:
# 检查在运行程序的时候后面是否跟了参数 hello
# 时间:
# 2023/04/30
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
if [ "${1}" == "hello" ]; then
echo -e "\n你好,最近过的好吗?\a"
elif [ -z "${1}" ]; then
echo -e "\n请在运行的程序后面添加【hello】这个参数, 例如:sh ${0} hello"
else
echo -e "\n您添加的参数错了,请添加hello这个参数, 例如:sh ${0} hello"
fi
执行结果(输入hello)
[root@localhost shelldir]# sh howareyou.sh hello
你好,最近过的好吗?
执行结果(什么也没输入)
[root@localhost shelldir]# sh howareyou.sh
请在运行的程序后面添加【hello】这个参数, 例如:sh howareyou.sh hello
执行结果(随便添加了一个参数)
[root@localhost shelldir]# sh howareyou.sh ppp
您添加的参数错了,请添加hello这个参数, 例如:sh howareyou.sh hello
接下来再来了解一个命令利用这个命令来做几个实验,学一个叫netstat的命令,这个命令可以查询到目前主机开启的网络服务端口,利用【netstat -tuln】来获取目前主机启动的服务
如果没有netstat这条命令可以使用
yum install -y net-tools来安装相关的命令
[root@localhost ~]# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
udp 0 0 127.0.0.1:323 0.0.0.0:*
udp6 0 0 ::1:323 :::*
输出的内容中,最重要的就是 【Local Address(本地主机的IP与端口对应)】那个字段,它代表的是本机所启动的网络服务,IP的部分说明的是该服务器位于哪个接口上。若为【127.0.0.1】则是针对本机开发,若是【0.0.0.0】或【:::】则代表对整个internet开放。每个端口都有其特点的网络服务,几个常见的端口与相关网络服务的关系是:
- 80:WWW
- 22:ssh
- 21:ftp
- 25:mail
- 111:RPC(远程过程调用)
- 631:CPUS(打印机服务功能)
假设我的主机要检测比较常见的21、22、25及80端口时,那我如何通过netstat去检测我的主机是否开启了这四个主要的网络服务端口?由于每个服务的关键词都是接在冒号【:】后面,所以可以使用类似【:80】的方式来检测
[root@localhost shelldir]# vim netstat.sh
# 程序说明:
# 使用netstat和grep进行检测 21、22、25及80端口是否存在
# 时间:
# 2023/04/30
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
testfile=/root/netstatcheck.txt
netstat -tuln > ${testfile}
testing=$(grep ":80" ${testfile})
if [ "${testing}" != "" ]; then
echo "存在80端口"
fi
testing=$(grep ":25" ${testfile})
if [ "${testing}" != "" ]; then
echo "存在25端口"
fi
testing=$(grep ":22" ${testfile})
if [ "${testing}" != "" ]; then
echo "存在22端口"
fi
testing=$(grep ":21" ${testfile})
if [ "${testing}" != "" ]; then
echo "存在21端口"
fi
例题2
条件判断式还可以搞的更复杂。举例来说,每当过年的时候都能收到压岁钱,写个脚本,每当我输入过年的时间的时候,都会提示我还有几个月过年。
- 先让用户输入过年的时间
- 用现在的时间对比过年时间
- 计算出离过年还剩下多少天
可以利用【date --date=“YYYYMMDD”+%s】转成秒数后,就很容易操作了
[root@localhost shelldir]# vim guonian.sh
#!/bin/bash
# 程序说明:
# 此程序是计算离过年还需要多长时间的
# 时间:
# 2023/04/29
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
# 用户输入过年时间判断是否为空以及是否为8个字符
read -p "请输入过年的时间,我帮你计算剩下多长时间过年如:(20240101):" date
date_panduan=$( echo ${date} | grep -E '^[0-9]{8}$' )
if [ "${date_panduan}" == "" ]; then
echo -e "\n请输入正确的日期\a"
exit 1
fi
# 计算出过年的时间
declare -i date_ustdin=$( date --date="${date_panduan}" +%s)
declare -i date_xianzai=$( date +%s)
declare -i date_jian=$((${date_ustdin} - ${date_xianzai}))
declare -i date_hsstdin=$( echo ${date_jian} /60/60/24 | bc)
# 输出现在离过年还需要多长时间
echo -e "距离过年还剩下"${date_hsstdin}"天"
上面的程序可以计算离过年还剩下多少天。其中的【${date_hsstdin}】变量中的 /60/60/24 就是将秒数换算成天数。
执行结果
[root@localhost shelldir]# sh guonian.sh
请输入过年的时间,我帮你计算剩下多长时间过年如:(20240101):20240101
距离过年还剩下242天
利用case…esac判断
上面提到的【if … then … fi】对于变量的判断是以【比对】的方式来分辨的,如果符合状态就进行某些操作,并且通过较多层次(就是 elif …)的方式来进行多个变量的程序代码编写。【case…esac】语句用于对变量或参数多重比较,如果匹配成功,就执行一段语句,否则执行其他语句。
它的语法格式如下
case $变量名称 in # 关键字为case,还有变量前面有美元符号
"第一个变量内容") # 每个变量内容建议用双引号括起来,关键字则为右圆括号
程序段
;; # 买个类别结尾使用两个连续的分号来处理
"第二个变量内容")
程序段
;;
*) # 最后一个变量内容都会用*来代表所有其他值
exit 1
;;
esac # 最终的case结尾,是【case】反过来写。
来修改以下上述的例题1,他应该会变成这样
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
case ${1} in
"hello")
echo "Hello,how,are,you?"
;;
"")
echo "你没有加参数hello例如:sh ${0} command"
;;
*) # 相当于通配符,0~无穷多个任意字符的意思
echo "你参数输入错了,应该输入hello"
;;
esac
执行结果
[root@localhost shelldir]# sh howareyou.sh
你没有加参数hello例如:sh howareyou.sh command
[root@localhost shelldir]# sh howareyou.sh dwda
你参数输入错了,应该输入hello
[root@localhost shelldir]# sh howareyou.sh hello
Hello,how,are,you?
我们Linux的【/etc/init.d】目录下有许多服务。例如有个名为netconsole的服务在该目录下,那么你想要重启该服务,可以这样做(要用root身份)
[root@localhost init.d]# /etc/init.d/netconsole restart
重点是restart,如果你使用【less /etc/init.d/netconsole】去查看一下,就会看到它使用的是case语法,并且会规定某些既定的变量内容。你可以执行/etc/init.d/netconsole,该脚本会告诉你有哪些后续接的变量可以使用。
一般来说,使用【case ${变量} in】这个语法时,当中的那个【${变量}】大致有两种获取方式:
- 直接执行式:例如上面提到的,利用【howareyou.sh hello】的方式来直接给予$1这个变量的内容,这也是在/etc/init.d/目录下的大多程序的设计方式
- 交互式:通过read这个命令来让用户输入变量内容
例题1
让用户输入one、two、three并且将用户的变量显示到屏幕上;如果输入的不是one、two、three就告诉用户只有这三种选择
这个是直接执行式
#!/bin/bash
# 程序说明:
# 直接执行程序,程序后面只能跟one、two、three三个参数
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
case ${1} in
"one")
echo "这是参数one"
;;
"two")
echo "这是参数two"
;;
"three")
echo "这是参数three"
;;
*)
echo "请输入"one,two,three"这些参数"
;;
esac
这个是交互式
#!/bin/bash
# 程序说明:
# 用户输入一个参数,程序判断有没有这个参数
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p "请选择参数:" choice
case ${choice} in
"one")
echo "这是参数one"
;;
"two")
echo "这是参数two"
;;
"three")
echo "这是参数three"
;;
*)
echo "请输入"one,two,three"这些参数"
;;
esac
利用function功能
什么是【函数(function)】功能?简单来说,其实,函数可以在shell脚本当中做出一个类似自定义执行命令的东西,最大的功能就是可以简化我们很多的程序代码。举例来说,上面的案例中,每个输入结果one、two、three其实输出的内容都一样,那么我就可以使用function来简化
function的语法是这样的
function fname () {
程序段
}
那个fname就是我们自定义的执行命令名称,而程序段就是我们要它执行的内容了。要注意的是,shell脚本的执行方式是由上到下,从左至右,因此在shell脚本当中的function的设置一定要在程序的最前面,这样才能够在执行时被找到可用的程序段
例题1
我们将上述的case…esac的例题改一下,自定义一个名为printit的函数来使用:
[root@localhost shelldir]# sh function.sh
#!/bin/bash
# 程序说明:
# 使用function来选择参数
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
function printit () {
echo -n "你选择的参数为:" # 加上-n可以不换行继续在同一行显示
}
echo "这是程序打印出你选择的参数:"
case ${1} in
"one")
printit; echo ${1} | tr 'a-z' 'A-Z' # 将输出的参数大小写转换
;;
"two")
printit; echo ${1} | tr 'a-z' 'A-Z'
;;
"three")
printit; echo ${1} | tr 'a-z' 'A-Z'
;;
*)
echo "你只能选择${0} {one|two|three}"
;;
esac
上面的例子中,声明了一个函数 prinit ,所以,当我们在后续的程序段中,只要执行printit的话,就表示我的shell脚本要去执行【function printit】里面的那几个程序段。
另外,function也拥有内置变量的,它的内置变量与shell脚本很类似,函数名称$0,后续接的变量也是以$1、$2..来替换,
【function fname () {程序段}】内的$0,$1…等与shell脚本的$0是不同的。
以上面的例题1来改变一下进行说明,假如我执行【sh function.sh one】,表示shell脚本内的$1为"one"这个字符串,但是在printit( )内的$1则与这个one无关。
#!/bin/bash
# 程序说明:
# 使用function来选择参数
# 时间:
# 2023/04/30
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shelldir
export PATH
function printit () {
echo -n "你选择的参数为:${1}"
}
echo "下面的程序会在屏幕上展示你可以选择的参数"
case ${1} in
"one")
printit 1
;;
"two")
printit 2
;;
"three")
printit 3
;;
*)
echo "你只能选择${0} {one|two|three}"
;;
esac
在上面的例子中,如果你输入【sh function.sh one】就会出现【你选择的参数为:1】如下图
因为我们在程序段落中,我们写了【printit 1】,那个1就会称为function当中的$1。