0
点赞
收藏
分享

微信扫一扫

第二章shell编程之条件语句

a.条件测试操作

  • Shell环境根据命令执行后的返回状态值($?)来判断是否执行成功
  • 当返回值为0时表示成功,否则(非 0值)表示失败或异常
  • 使用专门的测试工具-test命令,可以对特定条件进行测试,并根据返回值来判断条件是否成立返回值为0表示条件成立

使用 test 测试命令时,包括以下两种形式:

test 条件表达式

[ 条件表达式 ]

注意:

  • 这两种方式的作用完全相同,但通常后一种形式更为常用
  • 方括号“[”或“]”与条件表达式之间需要至少一个空格进行分隔

根据需要测试的条件类别不同,条件表达式也不同

比较常用的条件操作有:

  • 文件测试
  • 整数值比较
  • 字符串比较
  • 以及针对多个条件的逻辑测试

1)文件测试

文件测试指的是根据给定的路径名称,判断对应的是文件还是目录,或者判断文件是否可读、可写、可执行

文件测试的常见选项:

-d

测试是否为目录

-e

测试目录或文件是否存在

-f

测试是否为文件

-r

测试当前用户是否有权限读取

-w

测试当前用户是否有权限写入

-x

测试是否设置有可执行权限

执行条件测试操作后,通过预定义变量$?可以获得测试命令的返回状态值,从而判断该条件是否成立

例如,执行以下操作可测试目录/media/是否存在,如果返回值$?为0,表示存在此目录,否则表示不存在或者虽然存在但不是目录

[root@localhost w]#[ -d /media/ ]

[root@localhost ~]# echo $?           //查看前一命令的返回值

0                                                    //返回0表示条件成立

若测试的条件不成立,则测试操作的返回值将不为 0(通常为 1)

例如,执行以下操作展示了测试不存在目录的情况

[root@localnost ~]#[ -d /media/cdrom/senver ]

[root@localhost ~]# eche $?       //查看前一命令的返回值

1                                                //返回1表示条件不成立

为了更直观地査看测试结果,可以结合命令分隔符“&&”和echo 命令一起使用,当条件成立时直接输出“YES”

其中,“&&”符号表示“而且”的关系,只有当前面的命令执行成功后才会执行后面的命令,否则后面的命令将会被忽略

例如:

[root@localhost ~]#[ -d /media/cdrom/Server ] && echo "YES"

//无输出表示该目录不存在

[root@localhost ~]#[ -d /media/cdrom ] &8 echo "YES"

//输出"YES"表示该目录存在

YES

2)整数值比较

整数值比较指的是根据给定的两个整数值,判断第一个数与第二个数的关系,如是否大于、等于、小于第二个数

整数值常用选项:

-eq

第一个数等于第二个数

-ne

第一个数不等于第二个数

-gt

第一个数大于第二个数

-lt

第一个数小于第二个数

-le

第一个数小于或等于第二个数

-ge

第一个数大于或等于第二个数

  • 整数值比较在 Shell 脚本编写中的应用较多
  • 例如,用来判断已登录用户数量、开启进程数、磁盘使用率是否超标,以及软件版本号是否符合要求等
  • 实际使用时,往往会通过变量引用、命令替换等方式来获取一个数值

例如,若要判断当前已登录的用户数,当超过五个时输出“Too many.”,可以执行以下操作:

已登录用户数可通过“who wc-l”命令获得,以命令替换方式嵌入

[root@localhost ~]# Unum=`who | wc -l `     //查看当前已登录用户数

Lroot@localnost ~]# [ $Unum -gt 5 ] && echo "Too many."

                                                                   //测试结果(大于)

Too many

  • 若要判断物理内存(mem)当前的磁盘缓存(buff/cache)大小,当低于1024MB 时输出具体数值
  • “free -m”命令表示以 MB 为单位输出内存信息,提取的空闲内存数数值,可以执行以下操作
  • 其中,值通过命令替换赋值给变量Freeccc

[root@localhost ~]# Freecc $(free -m | grep "mem" awk ~{print $6}')

[root@localhost ~]# [ $Free0c lt 1024 ] && echo ${Frneecc}MB

3)字符串比较

字符串比较通常用来检査用户输入、系统环境等是否满足条件

在提供交互式操作的 shell脚本中,也可用来判断用广输入的位置参数是否符合要求

字符串比较常用的选项:

=

第一个字符串与第二个字符串相同

!=

第一个字符串与第二个字符串不相同,其中“!”符号表示取反

-z

检查字符串是否为空,对于未定义或赋予空值的变量将视为空串

例如,若要判断当前系统的语言环境,当发现不是“en.Us”时输出提示信息“Not en.us”,可以执行以下操作

[root@localhost ~]# echo $LANG

//查看当前的语言环境

zh_CN.UTF-8

[root@localhost ~]#[ $LANG != "en.us" ] && echo "Not en.us"

//字符串测试结果(不等于)

Not en.US

  • 在 Shell脚本应用中,经常需要用户输入“yes”或“no”来确认某个任务
  • 实际使用时还会根据变量“ACK”的取值分别执行进一步的操作

[root@localnost ~].# read -p  "是否覆盖现有文件(yes/no)?"  ACK

是否覆盖现有文件(yes/no)?yes

[root@localhost ~]#[ $ACK ="yes”] && echo "覆盖" 覆盖

[root@Localnost ~]# read -p "是否覆盖现有文件(yes/no)? ACK

是否覆盖现有文件(yes/no)?no

[root@localhost ~]#[ $AcK ="no“] && echo “不覆盖” 不覆盖

4)逻辑测试

  • 逻辑测试指的是判断两个或多个条件之间的依赖关系
  • 当系统任务取决于多个不同的条件时,根据这些条件是否同时成立或者只要有其中一个成立等情况,需要有一个测试的过程

常用的逻辑操作测试如下:

&&

逻辑与,表示而且,只有当前后两个条件都成立时,整个测试命令的返回值才为0,使用test命令时,&&可以改为-a

||

逻辑或,表示或者,只要当前后两个条件有一个成立时,整个测试命令的返回值就是0,使用test时,||可以改为-o

逻辑否,表示不,只有当指定的条件不成立时,整个测试命令的返回值才是0

例如,若要判断当前Linux系统的内核版本是否大于3.4,可以执行以下操作

其中,内核版本号通过 uname 和 awk 命令获得

[root@localhost ~]# uname -r      //査看内核版本信息

3.10.0-514.e17.x86 64

[root@localhost ~]# Mnum=$(uname -r l awk -F,'{print $1}')//取主版本号

[root@localhost ~]# Snum=$(uname -nlawk -F. {print $2}')  //取次版本号

[root@localhost ~]#[ $Mnum -ge 3 ] && [ $Snum -gt 4 ] && echo"符合要求”符合要求

b.if条件语句

使用专用的 if 条件语句,可以更好地整理脚本结构,使得层次分明,清晰易懂

1)if语句的结构

  • 在 She11 脚本应用中,if 语句是最为常用的一种流程控制方式
  • 用来根据特定的条件测试结果,分别执行不同的操作(如果.那么…)
  • 根据不同的复杂程度,if 语句的选择结构可以分为三种基本类型,适用于不同的应用场合

1.单分支语句

  • if 语句的“分文”指的是不同测试结果所对应的执行语句(一条或多条)
  • 对于单分支的选择结构,只有在“条件成立”时才会执行相应的代码,否则不执行任何操作

单分支 if 语句的语法格式:

if 条件测试操作

then

命令序列

fi

条件测试操作既可以是“[条件表达式]”语句,也可以是其他可执行的命令语句

命令序列指的是一条或多条可执行的命令行,也包括嵌套使用的if语或其他流程控制语句

执行流程:

  • 首先判断条件测试操作的结果,如果返回值为0,表示条件成立
  • 执行then 后面的命令序列,一直到遇见 fi结束判断为止,继续执行其他脚本代码
  • 如果返回值不为0,则忽略 then 后面的命令序列,直接跳至 fi行以后执行其他脚本代码

2.双分支语句

对于双分支的选择结构,要求针对“条件成立”“条件不成立”两种情况分别执行不同的操作

双分支if语的语法格式:

if 条件测试操作

then

命令序列 1

else

命令序列2

fi

执行流程:

  • 首先判断条件测试操作的结果,如果条件成立,则执行 then 后面的命令序列 1,忽略 else 及后面的命令序列 2,直到遇见 fi 结束判断
  • 如果条件不成立,则忽略 then 及后面的命令序列 1,直接跳至 else 后面的命令序列 2 并执行,直到遇见 f1 结束判断

3.多分支语句

由于 if 语句可以根据测试结果的成立、不成立分别执行操作,所以能够嵌套使用,进行多次判断

例如,首先判断某学生的得分是否及格,若及格则再次判断是否高于90分

多分支if 语句的语法格式:

if 条件测试操作 1

then

命令序列 1

elif 条件测试操作 2

then

       命令字列 2

else

       命令字列 3

fi

执行流程:

  • 首先判断条件测试操作1的结果,如果条件1成立,则执行命令序列1,然后跳至 fi 结束判断
  • 如果条件 1 不成立,则继续判断条件测试操作 2的结果
  • 如果条件 2 成立则执行命令序列 2,然后跳至 fì结束判断……
  • 如果所有的条件都不满足,则执行 else 后面的命令序列n,直到遇见fi结束判断

2)if语句应用示例

1.单分支if语句应用

若需要在 Shel1l脚本中执行挂载光盘的操作,建议先判断挂载点目录是否存在,若不存在则新建此目录

[root@localhost ~]# vim chkmountdir.sh

#!/bin/bash

MOUNT DIR="/media/cdrom

if [ !-d $MOUNT DIR ]

then

mkdir -P $MOUNT_DIR

fi

[root@1ocalhost~]# chmod  +x chkmoundir.sh

[root@localnost ~]#./chkmountdir.sh

例如,有些特权命令操作要求以root用户执行,如果当前用户不是root,那么再执行这些命令就没有必要

  • 针对这种情况,在脚本中可以先判断当前用户是不是root
  • 如果不是则报错并执行“exit 1”命令退出脚本(1 表示退出后的返回状态值),而不再执行其他代码

[root@localhost ~]# vim /opt/chkifroot.sh

#!/bin/bash

if [ "$USER” !- "root” ]

then

    echoc"错误!非 root 用户,权限不足!!"

    exit 1

fi

fdisk -l /dev/sda

[root@localhost ~]# chmod +x /opt/chkifroot.sh

当普通用户执行 chkifnoot.sh 脚本时,由于“非oot 用户”的条件成立,因此会提示权限不足并退出脚本

[jerry@centos-7 opt]$ /opt/chkifnoot.sh

错误!非 root 用户,权限不足!

[jerry@centos 7 opt]$

当 root 用户执行 chkifroot.sh 脚本时,由于“非 root 用户”的条件不成立,所以 if 语句不执行任何操作,正常执行 fi 之后的脚本代码

[root@localhost ~]# /opt/chkifroot.sh

…//省略部分内容

Device Boot Start End Blocks Id System

/dev/sda1 *2048 40894463 20446208 83 Linux

/dev/sda2 40894464 41943039 524288 82 Linux swap / solanis

2.双分支if语句应用

  • 双分支if语句只是在单分支的基础上针对“条件不成立”的情况执行另一种操
  • 而不是“坐视不管”地不执行任何操作

例如,若要编写一个连通性测试脚本 pinghost.sh,通过位置参数$1 提供目标主机地址,然后根据 ping 检测结果给出相应的提示

[root@localhost ~]# vim pinghost.sh

#!/bin/bash

ping -c 3 -10.2 -w 3 $1 8>/dev/null

if [ $? -eq o ]

then

       echo "Host $1 is up,"

else

        echo "Host $l is down."

fi

[root@localhost ~]# chmod +x pinghost. sh

  • -c:测试包数量
  • -i:间隔时间
  • -w:超时时间

通过“&>/dev/null”屏蔽了 ping 命令执行过程的输出信息

[root@localhost ,~]# ./pinghost.sh 192.168.4.11

Host 192.168.4.11 is up.

[root@localnost ~l]# ./pinghost.sh 192.168.4.13

Host 192.168.4.13 is down.

例如,通过 Shell 脚本检查 vsftpd 服务是否运行,如果已经运行则列出其监听地址、PID 号,否则输出提示“警告:vsftpd 服务不可用!”

其中,pgrep 命令的“-x”选项表示査找时使用精确匹配

[noot@localhost ~]# vim chkvsftpd.sh

#!/bin/bash

systemctl status vsftpd &>/dev/null

if  [ $?-eg 0 ]

then

       echo“监听地址: $(netstat -anpt l grep vsftpd l awk '{print $4}'}"

       echo "进程 PID 号:$(pgrep -x vsftpd)"

else

        echo"警告:vsftpd 服务不可用!"

fi

[root@localhost ~]# chmod +x chkvsftpd.sh

[root@localhost ~]# ./chkvsftpd.sh

警告:vsftpd 服务不可用!

[root@localhost~]# systemctl start vsftod

[root@localhost·~]#./chkvsftpd.sh

监听地址::::21

进程 PID 号:61003

3.多分支if语句应用

多分支 if 语句的结构能够根据多个互斥的条件分别执行不同的操作,实际上等同于嵌套使用的 if 语句

例如,若要编写一个成绩分档的脚本 gradediv.sh,根据输入的考试分数不同来区分优秀、合格、不合格

[root@localhost ~l# vim gradediv.sh

#!/bin/bash

read -p "请输入您的分数(8-100):”GRADE

if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]

then

      echo “$GRADE 分,优秀!"

elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ]

then

       echo “$GRADE 分,合格!"

else

        echo "$GRADE 分,不合格!"

fi

[root@localhost ~]# chmod +x gradediv.sh

[root@localnost ~]#./gnadediv.sh

请输入您的分数(6-100):67

67 分,不合格!

[root@localnost ~]# ./gradediv.sh

请输入您的分数(0-100):78

78 分,合格!

[root@localhost ~]# ./gradediv.sh

请输入您的分数(0-100):89

89 分,优秀!

c.case分支语句

1)case语句的结构

使用场景:

  • 某个变量存在多种取值,需要对其中的每一种取值分别执行不同的命令序列
  • 这种情况与多分支的if 语句非常相似,只不过if 语句需要判断多个不同的条件,而 case语句只是判断一个变量的不同取值

语法结构:

case 变量值 in

模式 1)

      命令序列 1

       ;;

模式 2)

       命令序列 2

       ;;

*)

       默认命令序列

esac

  • 关键字case 后面跟的是“变量值”,即“$变量名”
  • 整个分支结构包括在case..esac 之间
  • 中间的模式 1、模式 2、…、*对应为变量的不同取值(程序期望的取值)
  • 其中*作为通配符,可匹配任意值

执行流程:

  • 首先使用“变量值”与模式1进行比较,若取值相同则执行模式1后的命令序列,直到遇见双分号“;”后跳转至 esac,表示结束分文:
  • 若与模式 1不相匹配,则继续与模式 2进行比较,若取值相同则执行模式2后的命令字列,直到遇见双分号",”后跳转至 esac,表示结束分支.…
  • 依此类推,若找不到任何匹配的值,则执行默认模式“*)”后的命令序列,直到遇见 esac 后结束分支

case分支语语法结构句的特点:

case行尾必须为单词in,每一模式必须以右括号)结束

双分号;;表示命令序列的结束

模式字符串中,可以用方括号表示一个连续的范围,如[0-9]还可以用竖杠|表示或,如A|B

最后的*)表示默认模式,其中的*相当于通配符

2)case语句的应用示例

1.检查用户输入的字符类型

提示用户从键盘输入一个字符,通过case 语句判断该字符是否为字母、数字或者其他控制字符,并给出相应的提示信息

[root@localhost ~]# vim hitkey.sh

#!/bin/bash

read -p "请输入一个字符,并按 Enter 键确认:”KEY

case "$KEY" in

       [a-z]|[A-Z])       //匹配任意字母

       echo"您输入的是 字母。"

       ;;

        [0-9])              //匹配任意数字

        echo"您输入的是 数字。"

       ;;

         *)                  //默认模式,匹配任意字符

        ecno"您输入的是 空格、功能键或其他控制字符

esac

[root@Localnost ~]# chmod +x hitkey.sh

[root@localhost ~]#./hitkey.sh

请输入一个字符,并按 Enter 键确认:k             //输入字母k

您输入的是 字母.

[root@localhost ~]# ./hitkey.sh

请输入一个字符,并按Enter 键确认:8           //输入数字 8

您输入的是 数字。

[root@localhost ~]# ./hitkey.sh

请输入一个字符,并按 Enter 键确认:^[[19~    //按F8 键

您输入的是空格、功能键或其他控制字符.

2.编写系统服务的脚本

  • 编写一个名为 myprog 的系统服务脚本,通过位置变量$1 指定的 start、stop、restart、status控制参数,分别用来启动、停止、重启 sleep 进程,以及査看 sleep 进程的状态
  • 其中,命令 sleep 用来暂停指定秒数的时间,这里仅用做测试,在实际运维工作中应将 sleep 改为相应后台服务的控制命令序列

[root@locainost ~]# vim myprog

#!/bin/bash

case "$1” in

start)

         echo -n"正在启动 sleep 服务..."

          if sleep 7200 &

    then        //在后台启动 sleep 进程

         echo "OK"

     fi

     ;;

stop)

          echo -n "正在停止 sleep 服务....  ”pki11 "sleep" &>/dev/null

          echo "ok"      //停止上 sleep 进程

      ;;

status)

           if pgrep "sleep"  &> /dev/null ; then //判断光提示 s1eep 进程状态     

           echo "sleep 服务已经启动."  else

           echo "sleep 服务已经停止."

           fi

           ;;

restart)         //先停止、再启动服务

            $0 stop

            $0 start

             ;;

*)        //默认显示用发信息

           echo "用法:$0 {stant| stop |status l restart}"

esac

[root@localnost ~]# chmod +x myprog

[root@localhost ~]# ./myprog start

正在启动 sleep服务...OK

[root@localhost ~]# ,/myprog status

sleep 服务已经启动

[root@localhost ~]# ./myprog stop

正在停止 sleep 服务 ...OK

[root@localhost ~]# ./myprog reload

//未提供此参数,按默认处理

用法:./myprog {start|stoplstatuslrestart}

注意:

若要将 myprog 服务交给 systemd 来管理,还需要在/lib/systemd/system 目录下添加相应的myprog.service 配置文件

举报

相关推荐

0 条评论