Shell流程控制

  • 2019-11-18
  • 25
  • 0
  • 1

Shell 流程控制

:根据我们的命令退出码来进行判断(echo $? =0),如果是0,那么就会执行then后面的命令

单分支结构

判断/mnt目录是否存在

[root@localhost SHELL]# vim if-test.sh
#!/bin/bash
if ls /mnt
    then
        echo "yes"
fi

[root@localhost SHELL]# bash if-test.sh 
yes

双分支语句

判断root用户是否存在系统

例1:

[root@localhost SHELL]# vim if-test2.sh
#!/bin/bash
if grep root /etc/passwd;       then
        echo "yes"
else
        echo "no"
fi
[root@localhost SHELL]# bash if-test2.sh 
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
yes

例2:

[root@localhost SHELL]# vim if-test2.sh
#!/bin/bash
if grep root /Shuai;    then
        echo "yes"
else
        echo "no"
fi

[root@localhost SHELL]# bash if-test2.sh 
grep: /Shuai: 没有那个文件或目录
no

多分支语句

判断用户在系统中是否存在,是否有家目录

[root@localhost SHELL]# vim if-test3.sh

#!/bin/bash
read -p "请输入一个用户名:" uu
if grep $uu /etc/passwd;        then
        echo "$uu 存在系统"
elif ls -d /home/$uu;           then
        echo "$uu 不存在系统,但存在家目录"
else
        echo "$uu 不存在系统,也不存在家目录"
fi

[root@localhost SHELL]# bash if-test3.sh 
请输入一个用户名:shuai
shuai:x:1000:1000:shuai:/home/shuai:/bin/bash
shuai 存在系统

[root@localhost SHELL]# bash if-test3.sh 
请输入一个用户名:if-test
/home/if-test
if-test 不存在系统
if-test 存在家目录

[root@localhost SHELL]# bash if-test3.sh 
请输入一个用户名:ssss 
ls: 无法访问/home/ssss: 没有那个文件或目录
ssss 不存在系统,也不存在家目录

case

Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

case语句格式如下:

case  in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2
    command1
    command2
    ...
    commandN
    ;;
esac
case工作方式如上所示。取后面必须为单词in,(取值可以为变量或常数),每一模式必须以右括号结束。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

控制语句:用来实现对程序流程的选择、循环、转向和返回等进行控制。case是开关语句的一个组成部分;它是根据变量的不同进行取值比较,然后针对不同的取值分别执行不同的命令操作,适用于多分支,是一个多选择语句

执行流程:

首先使用“变量或表达式”的值与值1进行比较,若取值相同则执行值1后的命令序列,直到遇见双分号“;; ”后跳转至esac,表示分支结束;

若与值1不相匹配,则继续与值2 进行比较,若取值相同则执行值2 后的命令序列,直到遇见双分号“;; ”后跳转至esac,表示结束分支。

依次类推,若找不到任何匹配的值,则执行默认模式“ *) ”后的命令序列,直到遇见esac后结束分支

注意事项:

  • “变量或表达式”后面必须为单词in,每一个“变量或表达式”的值必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
  • 匹配中的值可以是多个值,通过“|”来分隔
[root@localhost SHELL]# vim case1.sh
#!/bin/bash
cat << EOF
****************
** 1.backup **
** 2.copy **
** 3.quit **
****************
EOF

read -p "请输入你的选择:" OP
case $OP in
        1 | backup)
                echo "BACKUP..." ;;
        2 | copy)
                echo "COPY..." ;;
        3 | quit)
                echo "QUIT" ;;
        *)
                echo "error"
esac
# 测试结果
[root@localhost SHELL]# bash case1.sh 
****************
**  1.backup  **
**  2.copy    **
**  3.quit    **
****************
请输入你的选择:1
BACKUP...
[root@localhost SHELL]# bash case1.sh 
****************
**  1.backup  **
**  2.copy    **
**  3.quit    **
****************
请输入你的选择:2
COPY...
[root@localhost SHELL]# bash case1.sh 
****************
**  1.backup  **
**  2.copy    **
**  3.quit    **
****************
请输入你的选择:3
QUIT
[root@localhost SHELL]# bash case1.sh 
****************
**  1.backup  **
**  2.copy    **
**  3.quit    **
****************
请输入你的选择:666
error
[root@localhost SHELL]# bash case1.sh 
****************
**  1.backup  **
**  2.copy    **
**  3.quit    **
****************
请输入你的选择:backup
BACKUP...

编写Apache启动脚本

[root@localhost SHELL]# vim case2.sh
#!/bin/bash
case $1 in
    start)
        systemctl $1 httpd
        ps aux | grep httpd
        echo "httpd start"
        ;;
    stop)
        systemctl $1 httpd
        ps aux | grep httpd
        echo "httpd stop"
        ;;
    restart)
        systemctl $1 httpd
        ps aux | grep httpd
        echo "httpd restart"
        ;;
    status)
        systemctl $1 httpd
        ;;
    *)
        echo "USAGE:$0 start | stop | restart"
esac
测试结果:
[root@localhost SHELL]# bash case2.sh start
root      12044  0.1  0.5 224056  5028 ?        Ss   18:31   0:00 /usr/sbin/httpd -DFOREGROUND
apache    12045  0.0  0.3 226140  3108 ?        S    18:31   0:00 /usr/sbin/httpd -DFOREGROUND
apache    12046  0.0  0.3 226140  3108 ?        S    18:31   0:00 /usr/sbin/httpd -DFOREGROUND
apache    12047  0.0  0.3 226140  3108 ?        S    18:31   0:00 /usr/sbin/httpd -DFOREGROUND
apache    12048  0.0  0.3 226140  3108 ?        S    18:31   0:00 /usr/sbin/httpd -DFOREGROUND
apache    12049  0.0  0.3 226140  3108 ?        S    18:31   0:00 /usr/sbin/httpd -DFOREGROUND
root      12094  0.0  0.0 112724   976 pts/0    S+   18:32   0:00 grep httpd
httpd start
[root@localhost SHELL]# 
[root@localhost SHELL]# bash case2.sh stop
root      12119  0.0  0.0 112724   976 pts/0    S+   18:32   0:00 grep httpd
httpd stop
[root@localhost SHELL]# bash case2.sh test
USAGE:case2.sh start | stop | restart

test测试命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试

格式:test 测试条件

如果结果是对的,也叫结果为真,用$?=0表示,反之为假,用非0表示

 数值比较

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ]
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ]
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ]
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ]
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ]
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ]

例1:

[root@localhost SHELL]# vim test1.sh
# 第一种写法
!/bin/bash
if test 2 -eq 1;        then
        echo "ok"
else
        echo "no"
fi

# 第二种写法(推荐使用这种写法)
if [ 2 -eq 2 ]; then
        echo "ok"
else
        echo "no"
fi

# 测试结果
[root@localhost SHELL]# bash test1.sh 
no
ok

例2:

[root@localhost SHELL]# vim test2.sh
#!/bin/bash
read -p "请输入两个值:" var1 var2
if [ $var1 -gt $var2 ];         then
        echo "$var1 > $var2"
elif [ $var1 -lt $var2 ];       then
        echo "$var1 < $var2"
else
        echo "$var1 = $var2"
fi

# 测试结果
[root@localhost SHELL]# bash test2.sh 
请输入两个值:11 22 
11 < 22
[root@localhost SHELL]# bash test2.sh 
请输入两个值:22 11
22 > 11
[root@localhost SHELL]# bash test2.sh 
请输入两个值:22 22
22 = 22

字符串比较

下表列出了常用的字符串运算符,假定变量 a 为 “aa”,变量 b 为 “bb”:

运算符 说明 举例
== 两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 字符串长度是否为0,不为0返回 true。 [ -n “$a” ] 返回 true。
str 字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

str1 > str2

str1大于str2为真 [ str1 \> str2 ]
str1 < str2 str1小于str2为真 [ str1 \< str2 ]

在做字符串大小比较的时候,注意字符串的顺序

  • 大于号和小于号必须转义,要不然SHELL会把它当成重定向符号
  • 大于和小于它们的顺序和sort排序是不一样的
  • 在test比较测试中,它使用的是ASCII顺序,大写字母是小于小写字母的;sort刚好相反
[root@localhost SHELL]# vim test3.sh
#!/bin/bash
read -p "请输入你的名字:" name
if [ $name == "root" ];         then
        echo "你是超级管理员"
else
        echo "你是普通用户"
fi

# 测试结果
[root@localhost SHELL]# bash test3.sh
请输入你的名字:root
你是超级管理员
[root@localhost SHELL]# bash test3.sh
请输入你的名字:shuai
你是普通用户

ASCII码

ASCII :((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646

var2=Test
if [ $var1 \> $var2 ];   then
        echo "$var1 > $var2"
else
        echo "$var1 < $var2"
fi

文件比较

属性检测描述如下:

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

 

[root@localhost SHELL]# vim test5.sh
#!/bin/bash
if [ -e /etc/passwd ];  then
        echo "ok"
else
        echo "no"
fi

[root@localhost SHELL]# bash test5.sh 
ok

第二种写法

[root@localhost SHELL]# test -e /etc/passwd && echo ok || echo no
ok

例2:日志清空

[root@localhost SHELL]# vim clear-log.sh
#!/bin/bash
# clear /var/log/messages
# 确定当前是root用户
if [ $USER != "root" ]; then
        echo "你必须使用root用户才能执行脚本"
        exit 10
fi

# 判断文件是否存在
if [ ! -f /var/log/messages ]; then
        echo "文件不存在"
        exit 12
fi

# 保留最近100行的日志内容
tail -100 /var/log/messages > /var/log/mesg.tmp

# 日志清理
> /var/log/messages
mv /var/log/mesg.tmp /var/log/messages
echo "日志已清空"
# 退出码 exit ,取值范围是0-255
[root@localhost SHELL]# bash clear-log.sh 
日志已清空

流程控制过程中复杂条件和通配符

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true

两个条件都为真或有一个为真就执行

判断第一种:

[ 条件判断一 ] && [ 条件判断二 ]

 判断第二种

[ 条件判断一 -a 条件判断二 ]

判断第三种

[[ 条件判断一 && 条件判断二 ]]

 例1:设置umask

参考:/etc/profile 

[root@localhost SHELL]# vim /etc/profile
if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
    echo "umask 002"
else
    echo "i am root :umask 022"
fi
# 测试结果
[root@localhost SHELL]# bash umask.sh
i am root :umask 022

[[  …  ]]和[  …  ]的区别

[[  …  ]] 运算符是[  …  ]运算符的扩充;[[  …  ]]能够支持 *,< 、>等符号且不需要转义符

当有两个[[  ]]方括号时:

[root@localhost SHELL]# if [[ $USER == r* ]]; then echo "hello,$USER"; else echo "$USER not"; fi
hello,root
: $USER == r*对比时, r* 表示以r开头的任意长度字符串,这样就包括root

也可以这样写:

[root@localhost SHELL]# if [[ $USER == [a-z]oot ]] ; then echo "hello,$USER" ; else echo $USER not ; fi
hello,root

当只有一个[  ] 方括号时:

[root@localhost SHELL]# if [ $USER == r* ]; then echo "hello,$USER"; else echo "$USER not"; fi
root not

:此时r* ,就表示两个字符串 r*

[[ 。。。 ]]和[ 。。。]的区别汇总:

  • 所有的字符与逻辑运算符直接用“空格”分开,不能连到一起。
  • 在[  …  ]表达式中,常见的> 、<需要加转义符\,大小比较
  • 进行逻辑运算符&&||比较时;如果用的[ ]符号,则用在外面,如[  …  ] && [  … ] || [  …  ]
    如果在[  …  ]里面进行逻辑与或的比较,则用-a-o进行表示,如[ x = y –a x < z –o x > m ]
  • [[  …  ]] 运算符只是[  …  ]运算符的扩充;能够支持< 、>符号运算不需要转义符;它还是以字符串比较大小。里面支持逻辑运算符 || 、 && , 不再使用-a 、-o
  • [[  …  ]] 用 && 而不是 -a 表示逻辑“与”;用 || 而不是 -o表示逻辑“或”
  • [[  …  ]]可以进行算术扩展,而[ … ]不可以
  • [[  … ]]能用正则,而[…]不行
  • 双括号(( ))用于数学表达式
  • 双方括号号[[ ]]用于高级字符串处理,比如“模糊匹配”

shell中的通配符

shell常见通配符:

字符 含义 实例
* 匹配 0 或多个字符 a*b  a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
? 匹配任意一个字符 a?b  a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b。
 
匹配 list 中的任意单一字符 a[xyz]b   a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb。
[!list]  匹配 除list 中的任意单一字符 a[!0-9]b  a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b  0与9之间必须也只能有一个字符 如a0b, a1b… a9b。
{string1,string2,…} 匹配 sring1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b    a与b之间只能是abc或xyz或123这三个字符串之一。

例:

[root@localhost SHELL]# ls /etc/*.conf
....

[root@localhost SHELL]# ls /etc/???.conf
/etc/nfs.conf /etc/ntp.conf /etc/sos.conf /etc/yum.conf

[root@localhost SHELL]# touch /opt/a{1,2,3}.txt

[root@localhost SHELL]# ls /opt/
a1.txt a2.txt a3.txt

[root@localhost SHELL]# ls /opt/a[123].txt
/opt/a1.txt /opt/a2.txt /opt/a3.txt

[root@localhost SHELL]# ls /opt/a[1,2,3].txt
/opt/a1.txt /opt/a2.txt /opt/a3.txt

[root@localhost SHELL]# ls /opt/a[1,3].txt
/opt/a1.txt /opt/a3.txt

实战:

1、编写脚本检查服务器运行状态

[root@localhost SHELL]# vim status.sh
#!/bin/bash
if [ $# -eq 1 ];        then
        systemctl status $1 > /dev/null
        if [ $? -eq 0 ];        then
                echo "$1"服务正在运行!
        else
                systemctl start $1
        fi
else
        echo "执行脚本格式"
        echo "sh $0 服务名"
fi
# 测试结果
[root@localhost SHELL]# bash status.sh sshd
sshd服务正在运行!
[root@localhost SHELL]# bash status.sh sshd xxx
执行脚本格式
sh status.sh 服务名
[root@localhost SHELL]# bash status.sh sshddddd
Unit sshddddd.service could not be found.
Failed to start sshddddd.service: Unit not found.

2、根据学生的成绩,判断优劣

[root@localhost SHELL]# vim check_cj.sh
#!/bin/bash
read -p "请输入你的成绩:" cj
if [ $cj -ge 0 ] && [ $cj -lt 60 ];     then
        echo "补考"
elif [ $cj -ge 60 ] && [ $cj -lt 70 ];  then
        echo "及格"
elif [ $cj -ge 70 -a $cj -lt 80 ];      then
        echo "良好"
elif [[ $cj -ge 80 && $cj -lt 100 ]];   then
        echo "优秀"
else
        echo "请输入0-100之间的成绩分数!"
fi
# 测试结果
[root@localhost SHELL]# bash check_cj.sh
请输入你的成绩:0
补考
[root@localhost SHELL]# bash check_cj.sh
请输入你的成绩:60
及格
[root@localhost SHELL]# bash check_cj.sh
请输入你的成绩:70
良好
[root@localhost SHELL]# bash check_cj.sh
请输入你的成绩:80
优秀
[root@localhost SHELL]# bash check_cj.sh
请输入你的成绩:520
请输入0-100之间的成绩分数!

3、数据备份!!!

要求:每周一晚上3:00 ,备份数据库服务器上/etc的所有文件到系统的/etcbak目录里,使用系统日期做备份文件名。

[root@localhost SHELL]# vim etcback.sh
#!/bin/bash
backdate=`date +%Y-%m-%d`
backdir=/etcbak
srcdir=/etc
[ -e $backdir ] || mkdir $backdir        # 判断目录是否存在,如果没有就创建
tar zcf ${backdir}/${backdate}-etc.tar.gz $srcdir     # {}也可以不写,但建议还是写
ls -lh $backdir/$backdate-etc.tar.gz
echo "backup etc is ok"
# 测试结果
[root@localhost SHELL]# bash etcback.sh 
tar: 从成员名中删除开头的“/”
-rw-r--r--. 1 root root 31M 11月 19 00:15 /etcbak/2019-11-19-etc.tar.gz
backup etc is ok
[root@localhost SHELL]# ls /etcbak/
2019-11-19-etc.tar.gz
[root@localhost SHELL]# chmod +x etcback.sh
[root@localhost SHELL]# crontab -e
0 3 * * * /root/SHELL/etcback.sh 2>&1 > /dev/null

																					
														
							
						

评论

还没有任何评论,你来说两句吧

发表评论

:zhenbang: :yiwen: :yinyue: :yinxian: :yi: :xxyl: :xinsui: :xiaonian: :weiqu: :tushe: :tu: :taiyang: :taikaixin: :shuijiao: :shenli: :shengqi: :ruo: :qianbi: :qian: :pen: :neng: :mianqiang: :meigui: :lu: :liwu: :kuanghan: :ku: :kaixin: :jinya: :jiku: :huaxin: :huaji: :hu: :heixian: :hehe: :han: :haha: :haha2: :guai: :dangao: :damuzhi: :daku: :chabei: :caihong: :bugaoxing: :bishi: :aixin: :a: :OK: :) :(