shell脚本if分支


很多时候必须要依据某些数据来判断程序该如何进行。
举例来说,我们在上头的 ans_yn.sh 讨论输入响应的范例中不是有练习当用户输入 Y/N 时,必须要执行不同的讯息输出吗?
简单的方式可以利用&& 与 ||,但如果我还想要执行一堆指令呢?那真的得要 if then了
if ... then是最常见的条件判断式,当符合某个判断条件的时候, 就进行某项工作。
if ... then的判断还有多层次的情况!我们分别介绍如下:
单层、简单条件判断式
如果你只有一个判断式要进行,那么我们可以简单的这样看:

            if [ 条件判断式 ]; then
              当条件判断式成立时,可以进行的指令工作内容;

            fi
            
fi为if反过来写,结束if之意!
如果有多个条件要判别时,除了将多个条件写入一个中括号内, 还可以有多个中括号来隔开!而括号与括号之间,则以 && 或 || 来隔开,意义是:
&& 代表 AND ;
|| 代表 or ;
在使用中括号的判断式中, &&及||就与指令下达的状态不同了。
举例来说, ans_yn.sh 里面的判断式可以这样修改:
[ "${yn}" == "Y" -o "${yn}" == "y" ]
上式可替换为
[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]
之所以这样改,很多人习惯一个中括号仅有一个判别式。
现在将ans_yn.sh这个脚本修改成为if ... then的样式来看看:
              [peter@study bin]$ cp ans_yn.sh ans_yn-2.sh
              [peter@study bin]$ vim ans_yn-2.sh
              #!/bin/bash
              # Program:
              # This program shows the user's choice
              # History:
              # 2015/07/16 INITroot First release
              PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
              export PATH
              read -p "Please input (Y/N): " yn
              if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
                echo "OK, continue"exit 0
              fi
              if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
                echo "Oh, interrupt!"
                exit 0
              fi
              echo "I don't know what your choice is" && exit 0
            
上面使用了两个条件判断!仅有一个${yn}的变量,为何需要进行两次比对呢? 此时,多重条件判断就来了!
多重、复杂条件判断式
在同一个数据的判断中,如果该数据需要进行多种不同的判断时,应该怎么作?
举例来说,上面的ans_yn.sh 脚本中,我们只要进行一次 ${yn} 的判断就好 (仅进行一次 if ),不想要作多次 if 的判断:
一个条件判断,分成功进行与失败进行 (else):
            if [ 条件判断式 ]; then
              当条件判断式成立时,可以进行的指令工作内容;
            else
              当条件判断式不成立时,可以进行的指令工作内容;
            fi
            
如果考虑更复杂的情况,则可以使用这个语法:
多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行
            if [ 条件判断式一 ]; then
              当条件判断式一成立时,可以进行的指令工作内容;
            elif [ 条件判断式二 ]; then
              当条件判断式二成立时,可以进行的指令工作内容;
            else
              当条件判断式一与二均不成立时,可以进行的指令工作内容;
            fi
            
elif也是个判断式,elif后面都要接then!但是else已经是最后的没有成立的结果了,所以else后面并没有then!
将 ans_yn-2.sh 改写成这样:
              [peter@study bin]$ cp ans_yn-2.sh ans_yn-3.sh
              [peter@study bin]$ vim ans_yn-3.sh
              #!/bin/bash
              # Program:
              #This program shows the user's choice
              # History:
              # 2015/07/16 INITroot First release
              PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
              export PATH
              read -p "Please input (Y/N): " yn
              if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
                echo "OK, continue"
              elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
                echo "Oh, interrupt!"
              else
                echo "I don't know what your choice is"
              fi
            
依序判断,避免掉重复判断的状况!
一般来说,如果不希望用户由键盘输入额外的数据时, 可以使用上一节提到的参数功能($1)!
让用户在下达指令时就将参数带进去!
现在我们想让用户输入hello关键词时,利用参数的方法可以这样依序设计:
1. 判断 $1 是否为 hello,如果是的话,就显示 "Hello, how are you ?";
2. 如果没有加任何参数,就提示用户必须要使用的参数下达法;
3. 而如果加入的参数不是 hello ,就提醒用户仅能使用 hello 为参数。
整个程序的撰写可以是这样的:
              [peter@study bin]$ vim hello-2.sh
              #!/bin/bash
              # Program:
              # Check $1 is equal to "hello"
              # History:
              # 2015/07/16 INITroot First release
              PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
              export PATH
              if [ "${1}" == "hello" ]; then
                echo "Hello, how are you ?"
              elif [ "${1}" == "" ]; then
                echo "You MUST input parameters, ex> {${0} someword}"
              else
                echo "The only parameter is 'hello', ex> {${0} hello}"
              fi
            
执行这支程序,分别在$1的位置输入hello, 没有输入与随意输入, 就可以看到不同的输出。
我们已经了grep,netstat指令可以查询到目前主机有开启的网络服务端口(service ports), 利用netstat -tuln来取得目前主机有启动的服务:
              [peter@study ~]$ netstat -tuln
              netstat -tuln
              Active Internet connections (only servers)
              Proto Recv-Q Send-Q Local Address           Foreign Address         State      
              tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN     
              tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
              tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
              tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
              tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN     
              tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN     
              tcp6       0      0 :::22                   :::*                    LISTEN     
              tcp6       0      0 ::1:631                 :::*                    LISTEN     
              tcp6       0      0 :::25                   :::*                    LISTEN     
              udp        0      0 0.0.0.0:38407           0.0.0.0:*                          
              udp        0      0 192.168.0.108:34361     0.0.0.0:*                          
              udp        0      0 192.168.0.108:55645     0.0.0.0:*                          
              udp        0      0 192.168.0.108:40197     0.0.0.0:*                          
              udp        0      0 127.0.0.53:53           0.0.0.0:*                          
              udp        0      0 0.0.0.0:68              0.0.0.0:*                          
              udp        0      0 0.0.0.0:631             0.0.0.0:*                          
              udp        0      0 0.0.0.0:5353            0.0.0.0:*                          
              udp6       0      0 :::35819                :::*                               
              udp6       0      0 :::5353                 :::*       
              #封包格式          本地IP:端口                 远程 IP:端口            是否监听 
            
上面的重点是Local Address (本地主机的 IP 与端口口对应)那个字段,他代表的是本机所启动的网络服务!
IP的部分说明该服务位于那个接口上,若为127.0.0.1则是仅针对本机开放,若是0.0.0.0 或 ::: 则代表对整个Internet开放 (更多信息请参考服务器架设篇的介绍)。
每个端口(port)都有其特定的网络服务,几个常见的port与相关网络服务的关系是:
80: WWW
22: ssh
21: ftp
25: mail
111: RPC(远程过程调用)
631: CUPS(打印服务功能)
假设主机要侦测的是比较常见的port 21, 22, 25 及80, 如何透过netstat去侦测主机是否有开启这四个主要的网络服务端口呢?
由于每个服务的关键词都是接在冒号:后面, 可以藉由撷取类似:80来侦测的!
可以简单的这样去写这个程序:
              [peter@study bin]$ vim netstat.sh
              #!/bin/bash
              # Program:
              # Using netstat and grep to detect WWW,SSH,FTP and Mail services.# History:
              # 2015/07/16 INITroot First release
              PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
              export PATH
              # 1. 先作一些告知的动作而已~
              echo "Now, I will detect your Linux server's services!"
              echo -e "The www, ftp, ssh, and mail(smtp) will be detect! \n"
              # 2. 开始进行一些测试的工作,并且也输出一些信息啰!
              testfile=/dev/shm/netstat_checking.txt
              netstat -tuln > ${testfile}              # 先转存数据到内存当中!不用一直执行 netstat
              testing=$(grep ":80 " ${testfile})       # 侦测看 port 80 在否?
              if [ "${testing}" != "" ]; then
                echo "WWW is running in your system."
              fi
              testing=$(grep ":22 " ${testfile})   # 侦测看 port 22 在否?
              if [ "${testing}" != "" ]; then
                echo "SSH is running in your system."
              fi
              testing=$(grep ":21 " ${testfile})   # 侦测看 port 21 在否?
              if [ "${testing}" != "" ]; then
                echo "FTP is running in your system."
              fi
              testing=$(grep ":25 " ${testfile})   # 侦测看 port 25 在否?

              if [ "${testing}" != "" ]; then
                echo "Mail is running in your system."
              fi
            
这样就可以看到主机有没有启动这些服务啦!
条件判断式还可以搞的更复杂!
写个脚本程序让用户输入放假日期,计算还有几天放暑假?
由于日期用相减的方式来处置,可以使用date显示日期与时间,转为由1970-01-01累积而来的秒数, 透过秒数相减来取得剩余的秒数后,再换算为日数即可。
整个脚本的制作流程有点像这样:
1. 先让用户输入他们的放假日期;
2. 再由现在日期比对放假日期;
3. 由两个日期的比较来显示还需要几天才能够放假的字样。
利用 date --date="YYYYMMDD" +%s 转成秒数:
              [peter@study bin]$ vim cal_retired.sh
              #!/bin/bash
              # Program:
              # You input your demobilization date, I calculate how many days before you demobilize.
              # History:
              # 2015/07/16 INITroot First release
              PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
              export PATH
              # 1. 告知用户这支程序的用途,并且告知应该如何输入日期格式?
              echo "This program will try to calculate :"
              echo "How many days before your demobilization date..."
              read -p "Please input your demobilization date (YYYYMMDD ex>20150716): " date2
              # 2. 测试一下,这个输入的内容是否正确?利用正规表示法啰~
              date_d=$(echo ${date2} |grep '[0-9]\{8\}')
              # 看看是否有八个数字
              if [ "${date_d}" == "" ]; then
                echo "You input the wrong date format...."
                exit 1
              fi
              # 3. 开始计算日期啰~
              declare -i date_dem=$(date --date="${date2}" +%s) # 放假日期秒数
              declare -i date_now=$(date +%s) # 现在日期秒数
              declare -i date_total_s=$((${date_dem}-${date_now})) # 剩余秒数统计
              declare -i date_d=$((${date_total_s}/60/60/24)) # 转为日数
              if [ "${date_total_s}" -lt "0" ]; then # 判断是否已放假
                echo "You had been demobilization before: " $((-1*${date_d})) " ago"
              else
                declare -i date_h=$(($((${date_total_s}-${date_d}*60*60*24))/60/60))
                echo "You will demobilize after ${date_d} days and ${date_h} hours."
              fi
            
可以计算放假日期,如果是已经放假, 还可以知道已经放假多久了
脚本中的date_d变量宣告那个/60/60/24是来自于一天的总秒数(24小时*60分*60 秒) 。
为了避免用户输入错误的数字,多了一个正规表示法的判断式。

initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读