Shell编程——if条件语句

if条件语句

1、if条件语句

1.1、if条件语句的语法

1.1.1、单分支结构

第一种语法:

bash 复制代码
if  <条件表达式>
  then
    指令
fi

第二种语法:

bash 复制代码
 if  <条件表达式>; then
     指令
 fi

上文的"<条件表达式>"部分可以是test、​​、​​\[​​]​、(())等条件表达式,甚至可以直接使用命令作为条件表达式。每个if条件语句都以if开头,并带有then,最后以fi结尾。

第二种语法中的分号相当于命令换行,上面的两种语法含义是相同的,读者可根据习惯自行选择。本书中主要使用第一种语法格式。

在所有编程语言里,if条件语句几乎是最简单的语句格式,且用途最广。当if后面的<条件表达式>成立时(真)​,就会执行then后面的指令或语句;否则,就会忽略then后面的指令或语句,转而执行fi下面的程序。

if单分支语句执行流程逻辑图如图所示。

条件语句还可以嵌套(即if条件语句里面还有if条件语句)​,注意每个if条件语句中都要有一个与之对应的fi(if反过来写)​,每个if和它下面最近的fi成对搭配,语法示例如下:

bash 复制代码
if  <条件表达式>
  then
    if  <条件表达式>
      then
        指令
    fi
fi

提示:通常在书写Shell条件语句编程时,要让成对的条件语句关键字的缩进相对应,以便于阅读浏览。

前文曾讲解过的文件条件表达式 -f "$file1" && echo 1就等价于下面的if条件语句。

bash 复制代码
if [ -f "$file1" ]; then
    echo 1
fi
1.1.2、双分支结构

if条件语句的单分支结构主体就是"如果...,那么..."​,而if条件语句的双分支结构主体则为"如果...,那么...,否则..."​。

if条件语句的双分支结构语法为:

bash 复制代码
if  <条件表达式>
  then
    指令集1
else
    指令集2
fi

前文的文件测试条件表达式 -f "$file1" && echo 1 ||echo 0就相当于下面的双分支的if条件语句。

bash 复制代码
if [-f "$file1" ]
  then
    echo 1
else
    echo 0
fi

if双分支语句执行流程逻辑图如图所示。

1.1.3、多分支结构

if条件语句多分支结构的主体为"如果...,那么...,否则如果...,那么,否则如果...,那么...,否则..."​。

if条件语句多分支语法为:

bash 复制代码
if  <条件表达式1>
  then
    指令1
elif <条件表达式2>
  then
    指令2
else
    指令3
fi
-----------------------多个elif----------------------------
if  <条件表达式1>
  then
    指令
elif <条件表达式2>
  then
    指令
elif <条件表达式3>
  then
    指令
    ... ...
else
    指令
fi

提示:

1)注意多分支elif的写法,每个elif都要带有then。

2)最后结尾的else后面没有then。

if多分支语句执行流程对应的逻辑图如图所示。

1.2、if条件语句多种条件表达式语法

前文已经说过,if条件语句(包括双多分支if)的"<条件表达式>"部分可以是test、​​、​​\[​​]​、(())等条件表达式,甚至还可以直接使用命令作为条件表达式,具体的语法如下。

  • (1)test条件表达式

    bash 复制代码
    if test 表达式
      then
        指令
    fi
  • (2)\[\]条件表达式

    bash 复制代码
    if [ 字符串或算术表达式 ]
      then
        指令
    fi
  • (3)\[]条件表达式

    bash 复制代码
    if [[ 字符串表达式 ]]
      then
        指令
    fi
  • (4)(())条件表达式

    bash 复制代码
    if ((算术表达式))
      then
        指令
    fi
  • (5)命令表达式

    bash 复制代码
    if 命令
      then
        指令
    fi

说明:以上表达式除了语法不同之外,具体的应用是一致的,实际工作场景中,读者只需选择一种适合自己习惯的表达式就好。

1.3、单分支if条件语句实践

范例:把下面测试文件中条件表达式的语句改成if条件语句。

bash 复制代码
[root@oldboy ~]# [ -f /etc/hosts ] && echo 1   #<==[](单中括号)条件表达式语法。
1
[root@oldboy ~]# [[ -f /etc/hosts ]] && echo 1  #<==[[]](双中括号)条件表达式语法。
1
[root@oldboy ~]# test -f /etc/hosts && echo 1  #<==test条件表达式语法。
1

参考答案:

bash 复制代码
[root@oldboy scripts]# cat 7_1.sh
if [ -f /etc/hosts ]              #<==[](单中括号)条件表达式改为if单分支语句。
  then
    echo "[1]"
fi
if [[ -f /etc/hosts ]]            #<==[[]](双中括号)条件表达式改为if单分支语句。
  then
    echo "[[1]]"
fi
if test -f /etc/hosts             #<==test条件表达式改为if单分支语句。
  then
    echo "test1"
fi

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 7_1.sh
[1]
[[1]]
test1

范例:开发Shell脚本判断系统剩余内存的大小,如果低于100MB,就邮件报警给系统管理员,并且将脚本加入系统定时任务,即每3分钟执行一次检查。

在解答示例之前,先介绍一些重要的内容:开发程序时,一定要重视分析问题及程序设计过程(例如:分模块实现第一关、第二关、第三关)​,确定需求并完成设计后,编码就是很容易的事情了。因此,请大家重视分析问题和程序设计的思想,而不要一上来就开始编码,这也是老男孩常说的,技术的提升仅是量的积累,思想的提升才是质的飞跃!

设计思路就是根据需求,把需求进行拆解,分模块逐步实现,例如本题可以分为如下几步:

  • 1)获取当前系统剩余内存的值(先在命令行实现)。
  • 2)配置邮件报警(可采用第三方邮件服务器)。
  • 3)判断取到的值是否小于100MB,如果小于100MB,就报警(采用if语句)。
  • 4)编码实现Shell脚本。
  • 5)加入crond定时任务,每三分钟检查一次。

本例的最终实现过程如下。

  • 1)获取内存大小,注意:内存大小是下面命令中对应buffers/cache那一行结尾的值(buffers)。

    bash 复制代码
    [root@oldboy scripts]# free -m
                  total         used         free       shared     buffers       cached
    Mem:            994          903           91            0         103          672
    -/+ buffers/cache:           127          867
    #<===这里的867是系统的剩余内存,因为Linux系统有缓冲和缓存的特性,即把系统多余的内存临时用于缓冲和缓存,以提升系统的性能和效率。
    Swap:             0            0            0
    [root@oldboy scripts]# free -m|awk 'NR==3 {print $NF}' #<===利用awk获取到867,默认单位为MB。
    867
  • 2)发邮件的客户端常见的有mail或mutt;服务端有sendmail服务(CentOS 5下默认的)、postfix服务(CentOS 6下默认的)。这里不使用本地的邮件服务,而是使用本地的mail客户端,以及第三方的邮件服务商,例如:163.com(需要提前注册用户),利用这个邮件账号来给接收报警的人发送报警邮件。

    bash 复制代码
    [root@oldboy scripts]# echo -e "set from=oldboy@163.com smtp=smtp.163.com\
    nset  smtp-auth-user=oldboy  smtp-auth-password=oldboy123  smtp-auth=login"  >> /etc/mail.rc   
    #<==配置mail.rc,将Linux本地作为邮件客户端,使用注册的163账号及smtp地址发送邮件。
    [root@oldboy scripts]# tail -2 /etc/mail.rc
    set from=oldboy@163.com smtp=smtp.163.com
    #<== from=oldboy@163.com为邮件地址,smtp=smtp.163.com为邮件服务器。
    set smtp-auth-user=oldboy smtp-auth-password=oldboy123 smtp-auth=login
    #<==oldboy为用户名,oldboy123为密码。
    [root@oldboy scripts]# echo "oldboy"|mail -s "title" oldboy@163.com
    #<==测试邮件发送(服务器要能上网)。
    [root@oldboy scripts]# echo oldboy >/tmp/test.txt #<==将正文放入文件。
    [root@oldboy scripts]# mail -s "title" oldboy@163.com </tmp/test.txt
    #<==读取文件内容并发送邮件。查看163邮件是否发送成功,本书是用自己的账号给自己发送邮件。
  • 3)编写Shell脚本。

bash 复制代码
[root@oldboy scripts]# cat 7_2.sh
FreeMem=`free -m|awk 'NR==3 {print $NF}'`  #<==获取系统当前的内存值,赋给变量FreeMem。
CHARS="Current memory is  $FreeMem."       #<==定义字符串CHARS变量,作为输出及供邮件正文使用。
if [ $FreeMem -lt 100 ]                    #<==[](单中括号)条件表达式语法,这是老男孩推荐的用法,如果小于100,则执行命令。
  then
    echo $CHARS|tee /tmp/messages.txt #<==屏幕输出提示,并写入文件。
    mail -s "`date +%F-%T`$CHARS" test@oldboyedu.com </tmp/messages.txt
                                           #<==发送邮件报警。
fi

提示:test@oldboyedu.com是报警邮箱,工作中应尽量使用企业邮箱,以防止邮件被屏蔽。

执行测试:

bash 复制代码
[root@oldboy scripts]# /bin/sh /server/scripts/7_2.sh
Current memory is 866.

提示:

  • 1) 测试时,我们没办法把内存调小,但可以把阈值调大,例如将100MB改成1000MB。
  • 2)注意,如果使用第三方邮件服务,则服务器本地无需开启邮件服务。

接下来,把上述脚本加入crond定时任务中,每3分钟检查一次,达到阀值就发邮件报警:

bash 复制代码
        [root@oldboy scripts]# crontab -l|tail -2
        # monitor sys mem at 20160813 by oldboy
        */3 * * * * /bin/sh /server/scripts/7_2.sh &>/dev/null

监测内存并报警有多种Shell写法,如下:

bash 复制代码
if (($FreeMem<1000))              #<==(())条件表达式语法。
  then
    echo $CHARS|tee /tmp/messages.txt
    #mail -s "`date +%F-%T`$CHARS" 490004487@qq.com </tmp/messages.txt
fi
if [[ $FreeMem -lt 1000 ]]         #<==[[]]条件表达式语法。
  then
    echo $CHARS|tee /tmp/messages.txt
    #mail -s "`date +%F-%T`$CHARS" 490004487@qq.com </tmp/messages.txt
fi
if test $FreeMem -lt 1000            #<==test条件表达式语法。
  then
    echo $CHARS|tee /tmp/messages.txt
    #mail -s "`date +%F-%T`$CHARS" 490004487@qq.com </tmp/messages.txt
fi

1.4、if条件语句的深入实践

范例:分别使用read读入及脚本传参的方式比较两个整数的大小。

参考答案1:使用单分支if语句和read读入实现整数大小的比较。

比较两个整数的大小有三种情况,因此用一个单分支if是无法实现的,需要3个单分支的if判断才行,每一个单分支if条件语句相当于前文的一个条件表达式判断,最终的脚本如下:

bash 复制代码
[root@oldboy scripts]# cat 7_3_1.sh
#! /bin/sh
read -p "pls input two num:" a b        #<==读入两个输入,分别赋值给变量a和b。
if [ $a -lt $b ]; then                  #<==如果$a小于$b,则执行命令。
    echo "yes, $a less than $b"         #<==打印输出,提醒用户。
    exit 0                              #<==判断完毕,成功执行,以0值退出脚本,此处如果不退出,则会继续执行下面的if语句,而这是不必要的。
fi
if [ $a -eq $b ]; then                  #<==如果$a等于$b,则执行命令,同理,成功后以0值退出脚本。
    echo "yes, $a equal $b"
    exit 0
fi
if [ $a -gt $b ]; then                  #<==如果$a大于$b,则执行命令,同理,成功后以0值退出脚本。
    echo "yes, $a greater than $b"
    exit 0
fi

本例执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 7_3_1.sh
pls input two num:6 2
yes,6 greater than 2
[root@oldboy scripts]# sh 7_3_1.sh
pls input two num:2 6
yes,2 less than 6
[root@oldboy scripts]# sh 7_3_1.sh
pls input two num:6 6
yes,6 equal 6

提示:此题目并未要求对传参个数及参数是否为整数进行判断,读者可以自己试一试,如果有问题,可以参考前文及后文的讲解。

上述使用if单分支语句比较参数大小的脚本有几个问题:

  • 没有对参数的个数及传入变量是不是整数做判断。
  • if条件语句多而杂,导致逻辑不够清晰。

那么有没有好一点的方法呢?当然有,见后文多分支if及case语句功能。

参考答案2:使用多分支if语句和利用read读入变量实现整数大小的比较。

bash 复制代码
[root@oldboy scripts]# cat 7_3_2.sh
#! /bin/sh
read -p "pls input two num:" a b         #<==读入两个输入,分别赋值给变量a和b。
if [ $a -lt $b ]; then                   #<==使用if多分支语法进行比较。
    echo "yes, $a less than $b"          #<==由于是一个if语句,因此就不需要退出脚本了。
elif [ $a -eq $b ]; then
    echo "yes, $a equal $b"
else [ $a -gt $b ]
    echo "yes, $a greater than $b"
fi

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 7_3_2.sh
pls input two num:8 4
yes,8 greater than 4
[root@oldboy scripts]# sh 7_3_2.sh
pls input two num:4 8
yes,4 less than 8
[root@oldboy scripts]# sh 7_3_2.sh
pls input two num:8 8
yes,8 equal 8

参考答案3:用脚本传参的方式比较整数大小。

对上文的read脚本进行简单修改即可实现传参的比较。单分支实现脚本如下:

bash 复制代码
[root@oldboy scripts]# cat 7_3_3.sh
#! /bin/sh
a=$1 #<==将脚本命令行的第一个参数赋值给变量a。
b=$2 #<==将脚本命令行的第二个参数赋值给变量b。
if [ $a -lt $b ]; then
   echo "yes, $a less than $b"
   exit 0
fi
if [ $a -eq $b ]; then
   echo "yes, $a equal $b"
   exit 0
fi
if [ $a -gt $b ]; then
   echo "yes, $a greater than $b"
   exit 0
fi

多分支实现脚本如下:

bash 复制代码
[root@oldboy scripts]# cat 7_3_4.sh
#! /bin/sh
a=$1 #<==将脚本命令行的第一个参数赋值给变量a。
b=$2 #<==将脚本命令行的第二个参数赋值给变量b。
if [ $a -lt $b ]; then
    echo "yes, $a less than $b"
elif [ $a -eq $b ]; then
    echo "yes, $a equal $b"
else [ $a -gt $b ]
    echo "yes, $a greater than $b"
fi

2、if条件语句企业案例精讲

2.1、监控Web和数据库的企业案例

范例:用if条件语句针对Nginx Web服务或MySQL数据库服务是否正常进行检测,如果服务未启动,则启动相应的服务。

这是企业级运维实战综合题,需要读者对Nginx Web服务或MySQL数据库服务很熟悉才行,本例同样适用于其他的Web服务和数据库服务。

大家还记得前面说过的开发程序前的三部曲吧?这里就采用这种方式来解答此题。

(1)分析问题

先想一想监控Web服务和MySQL数据库服务是否异常的方法有哪些,见表。

此外,对端口进程等进行判断时,尽量先通过grep过滤端口和进程特殊标记字符串,然后结合wc将过滤到的结果转成行数再比较,这样相对简单有效,且经过wc -l命令处理之后的结果一定是数字,这样再进行判断就会比较简便。如果单纯地根据具体的列取具体的值判断会很麻烦,如果确实想采用取值判断的方法,那就尽量用字符串比较的语法。

(2)监测MySQL数据库异常

  • 1)MySQL数据库环境准备,如下:
bash 复制代码
[root@oldboy ~]# yum install mysql-server -y
[root@oldboy ~]# /etc/init.d/mysqld start
正在启动 mysqld:                        [确定]
[root@oldboy ~]# netstat -lntup|grep mysql
tcp          0       0 0.0.0.0:3306         0.0.0.0:*         LISTEN       2275/mysqld
  • 2)通过命令行检测数据库服务是否正常,只有先确定命令行是正确的,才能确保将它放到脚本里也是正确的。

    首先采用端口监控的方式。在服务器本地监控端口的命令有netstat、ss、lsof,具体实现多种命令的方法如下:

    bash 复制代码
    [root@oldboy scripts]# netstat -lnt|grep 3306|awk -F "[ :]+" '{print $5}'
    #<==这个就是前面所描述的根据具体的列取值判断的方法,获取到值,然后看看其是否等于3306,老男孩不推荐采用这种取值方法,因为第一取值麻烦;第二如果使用数字比较,当端口不存在时就会报错,而一旦使用了取值判断方法, 即使看起来是数字,也要尽量使用字符串进行比较,否则你将掉进坑里,很久都不会爬出来。
    3306
    [root@oldboy scripts]# netstat -lntup|grep 3306|wc -l
    #<==过滤关键字端口,转成数字,此方法很好。
    1
    [root@oldboy scripts]# netstat -lntup|grep mysql|wc -l
    #<==过滤关键字进程,转成数字,此方法很好。
    1
    [root@oldboy scripts]# ss -lntup|grep mysql|wc -l
    #<==ss类似于netstat命令,参数选项可通用。
    1
    [root@oldboy scripts]# ss -lntup|grep 3306|wc -l
    #<==ss类似于netstat命令,参数选项可通用。
    1
    [root@oldboy scripts]# lsof -i tcp:3306|wc -l
    #<==利用lsof检查tcp协议的3306端口。
    2

    从远端监控服务器监控本地端口的命令有telnet、nmap、nc,这三个命令有可能需要事先安装好才能使用,安装方法为:

    bash 复制代码
    [root@oldboy scripts]# yum install telnet nmap nc -y
    [root@oldboy scripts]# nmap 127.0.0.1 -p 3306|grep open|wc -l
    #<==查看远端3306端口是否开通,过滤open关键字,结果返回1,说明有open关键字,表示3306端口是通的。
    1
    [root@oldboy scripts]# echo -e "\n"|telnet 127.0.0.1 3306 2>/dev/null|grep
    Connected|wc -l
    #<==telnet是常用来检测远端服务器端口是否通畅的一个好用的命令,在非交互时需要采用特殊写法才行,过滤的关键字为Connected,返回1,说明有Connected,表示3306端口是通的。
    1
    [root@oldboy scripts]# nc -w 2 127.0.0.1 3306 &>/dev/null
    #<==nc的命令很强大,这里用来检测端口。根据执行命令的返回值判断端口是否通畅,如果返回0,则表示通畅,-w为超时时间。
    [root@oldboy scripts]# echo $?
    0

    本例为了统一IP地址,因此使用的都是同一个IP,即127.0.0.1,在实际工作中,应该用自己服务器的IP来替代。

    下面对服务进程或进程数进行监控(适合本地服务器)​:

    bash 复制代码
    [root@oldboy scripts]# ps -ef|grep mysql|grep -v grep|wc -l
    3

    以下是在客户端模拟用户访问的方式进行监控。

    使用wget或curl命令访问URL地址来测试(如果要检测数据库是否异常,需要转为通过访问Web服务器去访问数据库)时,有三种判断思路。

    第一种是根据执行命令的返回值判断成功与否,本例的URL使用了网上的地址,在实际工作中应使用开发人员提供给我们的访问数据库的程序地址。

    bash 复制代码
    [root@oldboy  scripts]#  wget  --spider    --timeout=10  --tries=2  www.baidu.com
    &>/dev/null
    #<==在wget后面加url的检测方法,&>/dev/null表示不输出,只看返回值。--spider的意思是模拟爬取,--timeout=10的意思是10秒超时,--tries=2表示如果不成功,则重试2次。
    [root@oldboy scripts]# echo $?     #<==查看命令执行的返回值,0为成功。
    0
    [root@oldboy  scripts]#  wget  -T  10  -q  --spider  http://www.baidu.com  >&/dev/
    null  #<==用法同第一个wget, -q表示安静的。
    [root@oldboy scripts]# echo $?     #<==查看命令执行的返回值,0为成功。
    0
    [root@oldboy scripts]# curl -s -o /dev/null http://www.baidu.com
    #<==利用curl进行检测,-s为沉默模式,-o /dev/null表示将输出定向到空。
    [root@oldboy scripts]# echo $?     #<==查看命令执行的返回值,0为成功。
    0

    第二种是根据执行命令后获取到的字符串进行判断。此方法需要有前端动态程序支持,即开发PHP或Java程序从数据库里取出指定的字符串,看它和期待的是否相等。下面以PHP服务为例(对于一个PHP程序访问数据库,如果访问成功,则给出固定的输出)​。

    bash 复制代码
    [root@oldboy scripts]# /server/scripts/testmysql.php
    <? php
    /*
    #this scripts is created by oldboy
    #oldboy QQ:31333741
    #site: http://www.etiantian.org
    #blog: http://oldboy.blog.51cto.com
    */
        //$link_id=mysql_connect('主机名', '用户', '密码');
        $link_id=mysql_connect('localhost', 'root', 'matianhai') or mysql_error();
        //$link_id=mysql_connect('localhost', 'test', '');
        if($link_id){
            echo "mysql successful by oldboy ! ";
        }else{
            echo mysql_error();
        }
    ?>

    命令行执行:

    bash 复制代码
    [root@oldboy scripts]# php /server/scripts/testmysql.php #<==注意.php需执行
    (yuminstall php -y)安装
    mysql successful by oldboy !     #<==可以通过grep过滤这里输出的关键字,进而判断访问数据库是否成功。

提示:需要事先安装PHP软件才行,或者将程序放到Lnmp服务器的站点目录,然后curl或\wget访问http地址。此方法是监控数据库是否异常的最佳的方法。

3)开发监控MySQL数据库的脚本。

脚本1:

bash 复制代码
 #! /bin/sh
 echo method1-------------------
 if [ `netstat -lnt|grep 3306|awk -F "[ :]+" '{print $5}'` -eq 3306 ]
 #<==这里的判断思路不是很好,即①最好不要用整数进行比较,因为一旦端口不存在,取值就会为空,进行整数比较会报错。②不要根据列取具体的值,而是要过滤关键字,通过wc转成行数判断。
   then
     echo "MySQL is Running."
 else
     echo "MySQL is Stopped."
     /etc/init.d/mysqld start
 fi

脚本2:

bash 复制代码
echo method2-------------------
if  [  "`netstat  -lnt|grep  3306|awk  -F  "[  :]+"  '{print  $5}'`"  =  "3306"  ]
#<==用字符串的方式进行比较就好多了,避免了脚本1中整数比较的错误发生,但是取值麻烦了一些。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

脚本3:

bash 复制代码
echo method3-------------------
if  [  `netstat  -lntup|grep  mysqld|wc  -l`  -gt  0  ]  #<==过滤进程名,转成数字,很优秀的取值判断方法。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

脚本4:

bash 复制代码
echo method4-------------------
if [ `lsof -i tcp:3306|wc -l` -gt 0 ]  #<==过滤端口转成数字,很优秀的取值判断方法。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

脚本5:

bash 复制代码
echo method5-------------------
[ `rpm -qa nmap|wc -l` -lt 1 ] && yum install nmap -y &>/dev/null
#<==防止因nmap没有安装而导致的错误。
if [ `nmap 127.0.0.1 -p 3306 2>/dev/null|grep open|wc -l` -gt 0 ]
#<==远端的端口检查,推荐使用,同理,这里也不要过滤出open,然后再做字符比较,转成数字最佳。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

脚本6:

bash 复制代码
echo method6-------------------
[ `rpm -qa nc|wc -l` -lt 1 ] && yum install nc -y &>/dev/null
#<==防止因nc没有安装而导致的错误。
if [ `nc -w 2   127.0.0.1 3306 &>/dev/null&&echo ok|grep ok|wc -l` -gt 0 ]
#<==这个判断有点特别,即若nc执行成功,则输出和之后的过滤都没问题,最后转成数字,思路决定出路。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

脚本7:

bash 复制代码
echo method7-------------------
if [ `ps -ef|grep -v grep|grep mysql|wc -l` -gt 0 ]  #<==传统的过滤进程的方法,grep
                                                        -v grep是排除此命令自身。
  then
    echo "MySQL is Running."
else
    echo "MySQL is Stopped."
    /etc/init.d/mysqld start
fi

(3)监控Nginx Web服务异常

监控Nginx Web服务异常的方法和监控MySQL数据库一样,也是使用端口、进程或通过wget/curl访问来进行检测。

  • 1)Nginx web服务环境准备,如下:

    bash 复制代码
    [root@oldboy ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.
    com/repo/epel-6.repo
    [root@oldboy ~]# yum install nginx -y
    [root@oldboy ~]# /etc/init.d/nginx start
    正在启动 Nginx:                                                   [确定]
    [root@oldboy scripts]# echo oldboy >/usr/share/nginx/html/index.html
    #<==建立测试网页,内容为oldboy。
    [root@oldboy scripts]# curl http://127.0.0.1             #<==返回oldboy。
    oldboy
  • 2)通过命令行检测Nginx服务是否正常,同样只有命令行是正确的,才能确保它放到脚本里也是正确的。

    首先采用端口监控的方式。在监控Nginx和监控数据库端口时,除了端口不同,其他的命令方法都是一模一样的,想要了解详细注释,可参见前文监控数据库的代码注释。在服务器本地监控端口的命令有netstat、ss、lsof,如下:

    bash 复制代码
    [root@oldboy ~]# netstat -lnt|grep -w 80|awk -F "[ :]+" '{print $5}'
    80
    [root@oldboy ~]# netstat -lntup|grep -w 80|wc -l
    1
    [root@oldboy ~]# netstat -lntup|grep mysql|wc -l
    1
    [root@oldboy ~]# ss -lntup|grep mysql|wc -l
    1
    [root@oldboy ~]# ss -lntup|grep -w 80|wc -l
    1
    [root@oldboy ~]# lsof -i tcp:80|wc -l
    3

    在远端监控服务器本地端口的命令有telnet、nmap、nc,如下:

    bash 复制代码
    [root@oldboy ~]# nmap 127.0.0.1 -p 80|grep open|wc -l
    1
    [root@oldboy ~]# echo -e "\n"|telnet 127.0.0.1 80 2>/dev/null|grep
    Connected|wc -l
    1
    [root@oldboy ~]# nc -w 2 127.0.0.1 80 &>/dev/null
    [root@oldboy ~]# echo $?
    0

    对服务进程或进程数进行监控的方式(适合本地服务器)如下:

    bash 复制代码
    [root@oldboy ~]#   ps -ef|grep nginx|grep -v grep|wc -l
    2
    [root@oldboy ~]# ps -C nginx --no-header
        3147 ?          00:00:00 nginx
        3150 ?          00:00:00 nginx
    [root@oldboy ~]# ps -C nginx --no-header|wc -l
    2

    以下是在客户端模拟用户访问的监控方式。先通过wget或curl命令进行测试。执行wget或curl命令之后,再看返回值($? )​,为0,则成功。

    bash 复制代码
    [root@oldboy  ~]#  wget  --spider  --timeout=10  --tries=2  http://127.0.0.1  &>/
    dev/null
    [root@oldboy ~]# echo $?
    0
    [root@oldboy ~]# wget -T 10 -q --spider http://127.0.0.1 &>/dev/null
    [root@oldboy ~]# echo $?
    0
    [root@oldboy ~]# curl -s -o /dev/null http://127.0.0.1
    [root@oldboy ~]# echo $?
    0

    以下是获取字符串的方式(判断获取的字符串是否和事先设定的相等)​。

    bash 复制代码
    [root@oldboy ~]# curl http://127.0.0.1  #<==根据指定的返回值判断服务是否正常。
    oldboy

    以下是根据HTTP响应header的结果进行判断(200、301、302都表示正常)​。

    bash 复制代码
    [root@oldboy ~]# curl -I -s -w "%{http_code}\n" -o /dev/null http://127.0.0.1
    200

脚本1:

bash 复制代码
[root@oldboy scripts]# cat 7_5.sh
#! /bin/sh
echo http method1-------------------
if [ `netstat -lnt|grep 80|awk -F "[ :]+" '{print $5}'` -eq 80 ]
#<==取端口数字表 ,有Bug,不建议使用,如果非要取端口,则请改为字符串比较的语法。
  then
    echo "Nginx is Running."
else
echo "Nginx is Stopped."
/etc/init.d/nginx start
fi

脚本2:

bash 复制代码
echo http method2-------------------
if [ "`netstat -lnt|grep 80|awk -F "[ :]+" '{print $5}'`" = "80" ]
#<==字符串比较语法,取值太麻烦,不如过滤后转为行数再比较的方法好。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本3:

bash 复制代码
echo http method3-------------------
if [ `netstat -lntup|grep nginx|wc -l` -gt 0 ]
#<==通过检查端口的命令,过滤进程名(比过滤端口好)转成数字后再比较,这是推荐的用法。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本4:

bash 复制代码
echo http method4-------------------
if [ `lsof -i tcp:80|wc -l` -gt 0 ]
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本5:

bash 复制代码
echo http method5-------------------
[ `rpm -qa nmap|wc -l` -lt 1 ] && yum install nmap -y &>/dev/null
if [ `nmap 127.0.0.1 -p 80 2>/dev/null|grep open|wc -l` -gt 0 ]
#<==nmap检测方法,优势是适合从远端进行检查,推荐使用。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本6:

bash 复制代码
echo http method6-------------------
[ `rpm -qa nc|wc -l` -lt 1 ] && yum install nc -y &>/dev/null
if [ `nc -w 2   127.0.0.1 80 &>/dev/null&&echo ok|grep ok|wc -l` -gt 0 ]
#<==nc方法,前面已讲解过。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本7:

bash 复制代码
echo http method7-------------------
if [ `ps -ef|grep -v grep|grep nginx|wc -l` -ge 1 ]  #<==过滤进程方式,排除自身。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本8:

bash 复制代码
echo http method8-------------------
if  [[  `curl  -I  -s  -o  /dev/null  -w  "%{http_code}\n"  http://127.0.0.1`  =~
[23]0[012] ]]
#<== 获取状态码,然后做正则数字匹配(目标是200、301、302,但实际上多余了几个201、202、
      300),这是[[]]的特殊匹配用法之一,前文也讲过了。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

脚本9:

bash 复制代码
 echo http method9-------------------
 if  [  `curl  -I  http://127.0.0.1  2>/dev/null|head  -1|egrep  "200|302|301"|wc
-l` -eq 1   ]
 #<==此方法通过扩展的egrep过滤正确的状态码,然后利用wc转成数字,此方法比较优秀,推荐读者使用。
   then
     echo "Nginx is Running."
 else
     echo "Nginx is Stopped."
     /etc/init.d/nginx start
 fi

脚本10:

bash 复制代码
echo http method10-------------------
if [ "`curl -s http://127.0.0.1`" = "oldboy" ]
#<== 根据访问网站URL,将返回的结果和期待的值进行比较,这个方法略微麻烦,但是结果最准确,
    适用于数据库及更深层次的对网站集群后端各个应用的检测。
  then
    echo "Nginx is Running."
else
    echo "Nginx is Stopped."
    /etc/init.d/nginx start
fi

2.2、比较大小的经典拓展案例

范例:使用if条件语句比较两个整数的大小。使用传参方法时,需要对传参个数及传入的参数是否为整数进行判断。

参考答案:

bash 复制代码
[root@oldboy scripts]# cat 7_5_1.sh
#! /bin/bash
a=$1                                  #<==将脚本命令行的第一个参数赋值给变量a。
b=$2                                  #<==将脚本命令行的第二个参数赋值给变量b。
#no.1 judge arg nums.
if [ $# -ne 2 ]; then                 #<==判断传参个数。
    echo "USAGE:$0 arg1 arg2"         #<==若传参个数不符合要求,则打印提示后退出脚本。
    exit 2
fi
#no.2 judge if int
expr $a + 1 &>/dev/null              #<==判断变量a是否为整数。
RETVAL1=$?                            #<==获取expr命令的返回值并赋值给RETVAL1。
expr $b + 1 &>/dev/null              #<==判断变量b是否为整数。
RETVAL2=$?                            #<==获取expr命令的返回值并赋值给RETVAL2。
if [ $RETVAL1 -ne 0 -a $RETVAL2 -ne 0 ]; then
#<==执行判断,如果返回值不都为0,说明至少有一个变量不是整数,则打印提示,退出脚本。
    echo "please input two int again"
    exit 3
fi
#no.3 compart two num.
#<==经过参数个数判断及整数判断后,就可以放心地进行比较了。
if [ $a -lt $b ]; then
    echo "$a<$b"
elif [ $a -eq $b ]; then
    echo "$a=$b"
else
    echo "$a>$b"
fi

对于新手,老男孩不推荐使用if条件语句的复杂判断写法(逻辑不够清晰)​。

bash 复制代码
#! /bin/sh
read -p "Pls input two num:" a b
#no2 udge a and b if is int
expr $a + 0 &>/dev/null
RETVAL1=$?
expr $b + 0 &>/dev/null
RETVAL2=$?
if [ -z "$a" ] || [ -z "$b" ]
  then
    echo "Pls input two num agagin."
    exit 1
elif test $RETVAL1 -ne 0 -o $RETVAL2 -ne 0
  then
    echo "Pls input two "num" again."
    exit 2
elif [ $a -lt $b ]
  then
    echo "$a < $b"
elif [ $a -eq $b ]
  then
    echo "$a = $b"
else
    echo "$a > $b"
fi
exit 0

2.3、判断字符串是否为数字的多种思路

范例7-6:判断字符串是否为数字的多种思路。

参考答案1:使用sed加正则表达式。

判断思路:删除一个字符串中的所有数字,看字符串的长度是否为0,如果不为0,则说明不是整数。

bash 复制代码
[root@oldboy  scripts]#  [  -n  "`echo  oldboy123|sed  's/[0-9]//g'`"  ]  &&  echo
char || echo int
char
[root@oldboy scripts]# [ -n "`echo 123|sed 's/[0-9]//g'`" ] && echo char ||
echo int
int
[root@oldboy scripts]# [ -z "`echo 123|sed 's/[0-9]//g'`" ] && echo int ||
echo char
int
[root@oldboy scripts]# [ -z "`echo oldgirl123|sed 's/[0-9]//g'`" ] && echo
int || echo char
char

除了使用sed的替换外,还可以使用变量的子串替换等方法,如下:

bash 复制代码
[root@oldboy scripts]# num=oldboy521
[root@oldboy scripts]# [ -z "`echo "${num//[0-9]/}"`" ] && echo int || echo char
char
[root@oldboy scripts]# num=521
[root@oldboy scripts]# [ -z "`echo "${num//[0-9]/}"`" ] && echo int || echo char
int

参考答案2:变量的子串替换加正则表达式(特殊判断思路)​。

判断思路:如果num的长度不为0,并且把num中的非数字部分删除,然后再看结果是不是等于num本身,如果两者都成立,则num就是数字。

bash 复制代码
-n "$num"                   #<==如果num长度不为0,则表达式成立。
"$num" = "${num//[^0-9]/}" #<==把num中的非数字部分删除(即把字母删除),然后看结果是不是等于num本身,如果两者都成立,就是数字。
#例:对于非数字oldboy123,删除oldboy,就是123了,肯定不等于oldboy123。
#例:如果是数字123,删除非数字的部分,结果还是123,肯定等于123。

完整表达式如下:

bash 复制代码
[root@oldboy scripts]# num=521
[root@oldboy scripts]# [ -n "$num" -a "$num" = "${num//[^0-9]/}" ] && echo
"it is num"
it is num
[root@oldboy scripts]# num=oldboy521
[root@oldboy scripts]# [ -n "$num" -a "$num" = "${num//[^0-9]/}" ] && echo
"it is num"

参考答案3:通过expr计算判断(前文已用过多次了)​。

bash 复制代码
[root@oldboy scripts]# expr oldboy + 1 &>/dev/null    #<==把字符串或变量和整数相加看执行是否成功。
[root@oldboy scripts]# echo $?
2
[root@oldboy scripts]# expr 123 + 1 &>/dev/null
[root@oldboy scripts]# echo $?
0
[root@oldboy scripts]# expr 0 + 0 &>/dev/null
[root@oldboy scripts]# echo $?
1

参考答案4:利用"=~"符号判断

bash 复制代码
[root@oldboy scripts]# [[ oldboy123 =~ ^[0-9]+$ ]] && echo int || echo char
char
[root@oldboy scripts]# [[ 123 =~ ^[0-9]+$ ]] && echo int || echo char
int

参考答案5:利用bc判断字符串是否为整数(仅为参考方法,不推荐,因为有Bug)​。

bash 复制代码
[root@oldboy scripts]# echo oldboy|bc
0
[root@oldboy scripts]# echo oldboy123|bc
0
[root@oldboy scripts]# echo 123|bc      #<==返回结果为本身的就是整数。
123
[root@oldboy scripts]# echo 123oldboy|bc
(standard_in) 1: syntax error

2.4、判断字符串长度是否为0的多种思路

范例:实现判断字符串长度是不是为0的多种方法。

1)使用字符串条件表达式-z和-n的语法如下:

bash 复制代码
[root@oldboy scripts]# [ -z "oldboy" ] && echo 1 || echo 0
0
[root@oldboy scripts]# [ -n "oldboy" ] && echo 1 || echo 0
1

2)使用变量子串判断的语法如下:

bash 复制代码
[root@oldboy scripts]# char=oldboy
[root@oldboy scripts]# [ ${#char} -eq 0 ] && echo 1 || echo 0

3)使用expr length函数判断的语法如下:

bash 复制代码
[root@oldboy scripts]# [ `expr length "oldboy"` -eq 0 ] && echo 1 || echo 0
0

4)使用wc的-L参数统计判断的语法如下:

bash 复制代码
[root@oldboy scripts]# [ ` echo oldboy|wc -L ` -eq 0 ] && echo 1 || echo 0
0

5)使用awk length函数判断的语法如下:

bash 复制代码
[root@oldboy  scripts]#  [  `echo  oldboy|awk  '{print  length}'`  -eq  0  ]  &&
echo 1 || echo 0
0
相关推荐
和裕1 小时前
蜂窝板 vs 七层瓦楞重型纸箱:大件工业设备运输性能与成本全对比
大数据·运维·网络·人工智能·算法
Dawn-bit2 小时前
Linux 运维基础扩展:跳板机、堡垒机与物理服务器全流程
linux·运维·服务器·云计算·运维开发
新时代牛马2 小时前
Linux 定时任务:crontab、anacron 与systemd timer
linux·运维·服务器
mounter6252 小时前
Linux 7.2 引入更灵活的块设备错误注入机制
linux·linux kernel·kernel·disk·error injection
cpolar技术支持2 小时前
Docker 容器启动失败怎么查?端口、日志、权限与网络排障完整教程
linux·docker·容器·cpolar·网络排障
网硕互联的小客服3 小时前
怎样在CMD里用chkntfs /x C: 命令来取消磁盘自检?——原理与实践全解
运维·服务器·网络
布莱克6053 小时前
Linux 常用命令详解:从入门到实战
linux·运维·服务器
朽棘不雕4 小时前
可执行程序的格式
linux
IT摆渡者4 小时前
11Panel 部署雷池WAF
linux