选择结构if 只有条件判断结果为真时才执行相应的操作
循环结构 for、while/until 反复执行相同操作时,使用循环结构
分支结构case 根据变量值的匹配结果执行相应的操作
if选择结构
if选择结构-单分支
当'条件成立'时执行相应的操作
否则,不执行任何操作
格式1:
if 条件判断表达式;then 条件判断成立时,要执行的操作 fi
格式2
if 条件判断表达式 then 条件判断成立时,要执行的操作 fi
或者:
if [条件] then 指令 #条件判断成立时,要执行的操作 fi 或者 if [条件];then 指令 fi
例子1:
#!/bin/bash ping -c 3 localhost &> /dev/null if [ $? -eq 0 ];then echo aaaaa fi
例子2:
#!/bin/bash read -p "please input ip address " addr if [ -z $addr ];then echo "你没有输入要ping的ip地址" exit fi ping -c 3 $addr &> /dev/null if [ $? -eq 0 ];then echo "$addr is onlines" fi echo "script over!!!"
提示:分号相当于命令分号,上面两种语法等同。
特殊写法:if [ -f "$file1" ];then echo 1;fi相当于[ -f "$file1" ] && echo 1
下面举几个使用if条件句的例子
范例1:单分支if条件句整数比较大小
[root@localhost ~]#cat if_01.sh a=$1 b=$2 if [ $a -gt $b ] then echo "yes,$a >= $b" fi if [ $a -le $b ] then echo "yes,$a<=$b" fi [root@localhost ~]# sh if_01.sh 4 5 yes,4<=5
范例2:使用read及脚本传参如何实现上述比较
解答:
[root@localhost ~]# cat if_01.sh
read -p "pls input two numbers:" a b if [ $a -gt $b ] then echo "yes,$a >= $b" fi if [ $a -le $b ] then echo "yes,$a<=$b" fi
[root@localhost ~]# sh if_01.sh
pls input two numbers:100 99 yes,100 >= 99
提示:思考以上脚本有什么缺点?
范例3:实现如果/server/scripts下面存在if3.sh就打印if3.sh存在。(如果执行脚本后发现文件不存在,就创建这个脚本)
方法1:if条件句实现
[root@localhost scripts]# cat if4.sh #!/bin/bash FILEPATH="server/scripts" if [ -e "$FILEPATH/if3.sh" ] then echo "$FILEPATH/if3.sh is exist." fi if [ ! -e "$FILEPATH/if3.sh" ] then [ ! -d $FILEPATH ] && mkdir -p $FILEPATH [ -d $FILEPATH ] && { cd $FILEPATH touch if3.sh echo "if3.sh is touched." } fi
[root@localhost scripts]# sh -x if4.sh
+ FILEPATH=server/scripts + '[' -e server/scripts/if3.sh ']' + '[' '!' -e server/scripts/if3.sh ']' + '[' '!' -d server/scripts ']' + mkdir -p server/scripts + '[' -d server/scripts ']' + cd server/scripts + touch if3.sh + echo 'if3.sh is touched.' if3.sh is touched.
范例4:判断系统内存大小,低于100M就进行邮件报警
[root@localhost scripts]# free -m
total used free shared buffers cached Mem: 988 769 218 0 97 313 -/+ buffers/cache: 358 629 Swap: 2047 0 2047
[root@localhost scripts]# free -m|awk '/buffers// {print $NF}'
629
[root@localhost scripts]# cat judge_system.sh
cur_free=`free -m|awk '/buffers// {print $NF}'` chars="current memory is $cur_free." if [ $cur_free -lt 650 ] then echo $chars echo $chars|mail -s "chars" 12345678@qq.com fi
[root@localhost scripts]# sh judge_system.sh
current memory is 629.
用定时任务每分钟检查(如果每分钟检查的话,就不用输出了):
[root@localhost scripts]# cat judge_system.sh chars="current memory is $cur_free." if [ $cur_free -lt 650 ] then echo $chars|mail -s "chars" 12345678@qq.com fi
[root@localhost scripts]# crontab -e
#judge..... * * * * * /bin/bash /server/scripts/judge_system.sh >&/dev/null
if选择结构-双分支
当'条件成立'时执行命令序列1
否则,执行命令序列2
格式1:
if 条件判断表达式;then 条件判断成立时,要执行的操作 else 条件判断不成立时,要执行的操作 fi
格式2:
if 条件判断表达式 then 条件判断成立时,要执行的操作 else 条件判断不成立时,要执行的操作 fi
特殊写法:
if [ -f “$file1” ];then echo 1;else echo 0;fi相当于:[ -f “$file1” ] && echo 1 || echo 0
例子1:
如果httpd服务在运行着就输出 "网站服务正在运行"
如果httpd服务没有运行 就把网站服务开启
#!/bin/bash service httpd status &> /dev/null if [ $? -eq 0 ];then echo "网站服务正在运行" else service httpd start fi
例子2:
#!/bin/bash service sshd status &> /dev/null if [ $? -eq 0 ];then echo "sshd in running" else service sshd start fi
例子3:
检查任意一个服务的状态
如果服务是运行着的,就输出服务名is running
如果服务没有运行,就启动服务。
#!/bin/bash service $1 status &> /dev/null if [ $? -eq 0 ];then echo "$1 is running!!!!" else service $1 start fi
只检查服务vsftpd httpd sshd crond mysql中的任意一个服务的状态,如果不是这5个,就提示用户能够检查服务名并退出脚本
#!/bin/bash read -p "请输入服务名" servername if [ $ servername=="vsftpd" -o $servername=="httpd" -o $servername="sshd" -o $servername="crond" -o $servername=mysql];then service $servicename status &> /dev/null if [ $? -eq 0 ];then echo $ servicename is running else service $servicename start fi fi
根据学生输入的成绩判断学生成绩的优劣。学生成绩的分数范围在1~100区间。成绩分数小于60输出不及格;成绩分数大于等于60输出 及格
read -p "请输入学生成绩:" chengji if [ -z $chengji ];then exit fi if [ $chengji -ge 1 ]&& [ $chengji -le 100 ] then if [ $cengji -lt 60 ] then echo "不及格" else echo "及格" fi else echo "输入格式错误" fi
#ifelse_exam1脚本用于判断输入的文件名是否有对应的文件存在
#!/bin/bash #if语句用于判断输入的文件是否不存在,不存在则执行then和else间的命令 if [ ! -e "$1" ] then echo "file $1 do not exist." exit 1 #输入的文件存在时,执行else和fi间的命令 else echo "file $1 exist." fi
#ifelse_exam2脚本用于删除一个文件并判断是否执行了该操作
#!/bin/bash #提示用户输入要删除的文件 echo "Please input the file which you want to delete:" read file #通过if/else结构判断文件是否被删除 if rm -f "$file" then echo "Delete the file $file successfully!" else echo "Delete the file $file failed!" fi
每周一的 23:30 备份当前数据库服务器上所有的binlog日志文件到系统的/binlogdir目录里.
#!/bin/bash mysql="/var/lib/mysql" date=(date +\%Y\%m\%d) cd $mysql if [ -e /binlogdir ];then tar -zcf /binlogdir/binlog-$date.tar.gz data-bin.* &> /dev/null else mkdir /binlogdir tar -zcf /binlogdir/binlog-$date.tar.gz data-bin.* &> /dev/null fi
if选择结构-多分支
相当于if语句嵌套
针对多个条件分别执行不同的操作
格式1:
if 条件判断表达式1;then 条件1判断成立时,要执行的操作
elif 条件判断表达式2;then 条件2判断成立时,要执行的操作
if 条件判断表达式N;then 条件N判断成立时,要执行的操作
else 以上所有的条件判断都不成立时,要执行的操作 fi
根据学生输入的成绩判断学生成绩的优劣。
学生成绩的分数范围在1~100区间
成绩 分数小于60 输出 不及格
成绩 分数大于等于60且小于80 输出 良好
成绩 分数大于80且小于100 输出 优秀
如果输入的成绩超出成绩区间,就输出提示信息:成绩的分数范围在1~100区间
#!/bin/bash read -p "输入成绩" cj if [ -z $cj ];then echo "你没有输入成绩分数" exit else if [ $cj -eq 0 ];then echo "补考" elif [ $cj -lt 0 ];then echo "输入的成绩超过范围,有效范围在1-100"
elif [ $cj -ge 1 ]&&[ $cj -lt 60 ];then echo "不及格"
elif [ $cj -ge 60 ]&&[ $cj -lt 80 ];then echo "良好"
elif [ $cj -ge 80 ]&&[ $cj -le 100 ];then echo "优秀"
elif [ $cj -gt 1 ];then echo "输入的成绩超过范围,有效范围在1-100" fi fi
练习:写一个脚本
传递一个参数(单字符就行)给脚本,如参数为q、Q、quit或Quit,就退出脚本;否则,就显示用户的参数;
#!/bin/bash # if [ $1 = 'q' ];then echo "Quiting..." exit 1 elif [ $1 = 'Q' ];then echo "Quiting..." exit 2 elif [ $1 = 'quit' ];then echo "Quiting..." exit 3 elif [ $1 = 'Quit' ];then echo "Quiting..." exit 4 else echo $1 fi
#ifelifelse_exam1.sh脚本
#!/bin/bash #提示用户输入分数(0~100) echo "Please Input a integer(0-100): " read score #判断学生的分数类别 if [ "$score" -lt 0 -o "$score" -gt 100 ] then echo "The score what you input is not integer or score is not in (0-100)" elif [ "$score" -ge 90 ] then echo "The grade is A!" elif [ "$score" -ge 80 ] then echo "The grade is B! " elif [ "$score" -ge 70 ] then echo "The grade is C!" elif [ "$score" -ge 60 ] then echo "The grade is D! " else echo "The grade is E! " fi
判断是否是闰年
#ifelifelse-exam2.sh脚本的执行结果 #!/bin/bash #提示输入年份 echo "Please input a year: " read year #设置取余参数 let "n1=$year % 4" let "n2=$year % 100" let "n3=$year % 400" #判断输入的年份是否是闰年 if [ ! "$n1" -eq 0 ] then leap=0 elif [ ! "$n2" -eq 0 ] then leap=1 elif [ ! "$n3" -eq 0 ] then leap=0 else leap=1 fi #输出用户输入的年份是否是闰年 if [ "$leap" -eq 1 ] then echo "$year is a leap year!" else echo "$year is not a leap year!" fi
范例5:多分支if条件句整数比较大小
[root@localhost scripts]# cat if_02.sh read -p "pls input two numbers:" a b if [ $a -gt $b ] then echo "yes,$a > $b" elif [ $a -eq $b ] then echo "yes,$a=$b" else echo "yes,$a<$b" fi
[root@localhost scripts]# sh if_02.sh
pls input two numbers:4 5
yes,4<5
范例6:以命令行脚本传参的方式比较两个数的大小,请用多分支if语句来实现。
法1:多分支if语句实现对传入的参数进行判断的方法
[root@localhost scripts]# cat if_02.sh
a=$1 b=$2 if [ $# -ne 2 ] then echo "Usage:sh $0 num1 num2" exit 1 fi if [ $a -gt $b ] then echo "yes,$a > $b" elif [ -eq $b ] then echo "yes,$a=$b" else echo "yes,$a<$b" fi
法2:多分支if语句实现对参数个数及类型严格判断的方法
[root@localhost scripts]# cat if_02.sh a=$1 b=$2 if [ $# -ne 2 ] then echo "Usage:sh $0 num1 num2" exit 1 fi [ -n "`echo $1|sed ' s/[0-9]//g'`" ]&& echo "第一个参数必须为数字" && exit 1 [ -n "`echo $2|sed ' s/[0-9]//g'`" ]&& echo "第二个参数必须为数字" && exit 1 if [ $a -gt $b ] then echo "yes,$a > $b" elif [ -eq $b ] then echo "yes,$a=$b" else echo "yes,$a<$b" fi
扩展:判断字符串是否为数字的多种方法
法1:sed加正则表达式
命令行传参:
组合语法判断1:
[ -n "`echo $num|sed ' s/[0-9]//g'`" -a -n "`echo $2|sed ' s/[0-9]//g'`" ] &&
echo "两个参数都必须为数字" && exit 1
组合语法判断2:
[ -n "`echo $num|sed ' s/[0-9]//g'`" -a -n "`echo $2|sed ' s/[0-9]//g'`" ] &&{ echo "两个参数都必须为数字 exit 1 }
单个判断语法:
[ -n "`echo $1|sed ' s/[0-9]//g'`" ] && echo "第一个参数必须为数字" && exit 1 [ -n "`echo $2|sed ' s/[0-9]//g'`" ] && echo "第二个参数必须为数字" && exit 1
普通变量判断:
[ -n "`echo $num|sed ' s/[0-9]//g'`" ] && echo "第二个参数必须为数字" && exit 1
条件表达式,大括号的用法:
[ -n "`echo $num|sed ' s/[0-9]//g'`" ] && { echo "第二个参数必须为数字" exit 1 }
法2:变量的子串替换加正则表达式
[root@localhost scripts]# num=oldboy512
[root@localhost scripts]# [ -z "`echo "${num//[0-9]/}"`" ] && echo 1 || echo 0
0
[root@localhost scripts]# num=512old512
[root@localhost scripts]# [ -z "`echo "${num//[0-9]/}"`" ] && echo 1 || echo 0
0 #这个说明前面的结果不为0,即有非数字字符
[root@localhost scripts]# num=521
[root@localhost scripts]# [ -z "`echo "${num//[0-9]/}"`" ] && echo 1 || echo 0
1 #这个说明前面的结果去掉数字后为0,即没有非数字字符
法3:变量的子串替换加正则表达式(特殊判断思路)
思路:如果num长度不为0,并且把num中的非数字部分删除,然后看结果是不是等于num本身,如果两者都成立就是数字。
-z “$num” #如果num长度不为0表达式 “$num” ="${num//[^0-9]/}" #把num中的非数字部分删除,然后结果是不是等于num本身,如果两者都成立就是数字。
完整表达式:
[root@localhost scripts]# [ -n "$num" -a "$num" = "$num//[^0-9]/" ] && echo "it is num"
法4:expr计算判断
expr $1 + 0>/dev/null 2>&1 #把变量和整数相加看是否成功执行 [ $? -eq 0 ] && eccho init
范例5:通过read读入变量方式的多分支if语句比较整数大小
[root@localhost scripts]#cat if-multi.sh #!/bin/bash #this scripts is created by oldboy #fun:compare two num read -t 10 -p "pls input two num:" a b if [ $a -gt $b ];then echo "yes,$a > $b" elif [ -eq $b ];then echo "yes,$a=$b" else echo "yes,$a<$b" fi
另一种写法:用(())代替[]
[root@localhost scripts]#cat if-multi.sh #!/bin/bash #this scripts is created by oldboy #fun:compare two num read -t 10 -p "pls input two num:" a b if (( $a > $b ));then echo "yes,$a > $b" elif [ -eq $b ];then echo "yes,$a=$b" else echo "yes,$a<$b" fi
特别提示:一般生产运维中的脚本,使用read的方式很少,除非为用户写交互式程序脚本。一般为传参或者定义变量的方式。
范例6:生产环境监控MySQL服务的实战例子
问题描述:监控MySQL服务是否正常启动,如果没有正常启动,就启动MySQL服务。这里需要大家实现准备MySQL服务的基本环境。
法1:本方法的实现思路是过滤出MySQL端口3306进行判断。具体脚本如下:
[root@localhost script]# cat check_db.sh #!/bin/bash ### portNum=`netstat -lnt | grep 3306|awk -F '[ :]+' '{print $5}'` #这里的思路不是最佳的 if [ #portNum -eq 3306 ];then #这里的判断最好用字符串判断的语法;假如端口不存在,显示[: -eq: unary operator expected echo "db is running" else /data/3306/mysql restart #这是多实例MySQL启动脚本 fi
思考:本脚本的好处和局限是什么?
或者:
[root@localhost script]# cat check_db.sh
#!/bin/bash ### portNum=`netstat -lnt | grep 3306|awk -F '[ :]+' '{print $5}'` if [ $portNum -eq 3306 ];then echo "db is running" else /etc/init.d/mysqld restart #普通MySQL服务启动 fi
解决-eq: unary operator expected的方法:不规范;对于多实例脚本启动有效,但对普通MYSQL启动问题仍然存在
[root@localhost script]# cat check_db01.sh #!/bin/bash ### portNum=`netstat -lnt | grep 3306|awk -F '[ :]+' '{print $5}'` if [ $portNum == “3306” ];then echo "db is running" else /data/3306/mysql restart fi
通过端口判断更简易实现脚本
[root@localhost script]# cat check_db02.sh #!/bin/bash ### portNum=`netstat -lnt | grep 3306|wc -l` if [ #portNum -eq 1 ];then echo "db is running" else /data/3306/mysql restart fi
法2:本方法的实现思路是如果MySQL端口和端口同时存在,即认为MySQL服务正常。
(1)写脚本前首先人工检查MySQL服务是否OK
[root@localhost script]# /data/3306/mysql start 正在启动 mysqld:
[root@localhost ~]# netstat -lnt | grep 3306
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
[root@localhost script]# ps -ef|grep mysql|grep -v grep|wc -l
2 #通过把进程转换成为行数,即进程个数,方便做判断。
[root@localhost ~]# netstat -lnt | grep 3306|wc -l
1 #通过把端口转换为行数,即端口个数,方便做判断
提示:根据以上结果可以知道MySQL服务是正常的(多数情况下)。
(2)实际的脚本监控内容
方法1:
[root@localhost script]# cat check_db03.sh #!/bin/bash ### portNum=`netstat -lnt | grep 3306|wc -l` mysqlProcessNum=`ps -ef|grep mysql|grep -v grep|wc -l` if [ #portNum -eq 1 -a $mysqlProcessNum -eq 2 ];then echo "db is running" else /data/3306/mysql restart fi
或者
[root@localhost script]# cat check_db03.sh #!/bin/bash ### portNum=`netstat -lnt | grep 3306|wc -l` mysqlProcessNum=`ps -ef|grep mysql|grep -v grep|wc -l` if [ #portNum -eq 1 ] && [ $mysqlProcessNum -eq 2 ];then echo "db is running" else /data/3306/mysql restart fi
方法2:
[root@localhost script]# cat if-judge-db0.sh #!/bin/bash ### MYSQL=/data/3306/mysql LogPath=/tmp/mysql.log portNum=`netstat -lnt | grep 3306|wc -l` mysqlProcessNum=`ps -ef|grep mysql|grep -v grep|wc -l` if [ #portNum -eq 1 ] && [ $mysqlProcessNum -eq 2 ];then echo "db is running" else $MYSQL start >$LogPath sleep 10 portNum=`netstat -lnt | grep 3306|wc -l` mysqlProcessNum=`ps -ef|grep mysql|grep -v grep|wc -l` if [ #portNum -ne 1 ] && [ $mysqlProcessNum -ne 2 ];then while true do killall mysqld >/dev/null 2>&1 [ $? -ne 0 ] && break sleep 1 done $MYSQL start >>$LogPath && status="successful" || status="failure" mail -s "mysql startup status is $status" 1092327070@qq.com < $LogPath fi fi
法3:本方法的实现思路是:模拟web服务器,通过mysql账户连接mysql,然后根据返回命令状态或返回内容确认mysql是否正常(推荐)
(1)手动检查(写脚本之前都离不开手工检查):
[root@localhost script]# mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();"
(1)简单的脚本实现:
法1:
法2:
[root@localhost script]# cat check_db.sh #!/bin/bash ### mysqlStatus=`mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();" >& /dev/null` if [ $? -eq 0 ];then echo "db is running" else /data/3306/mysql restart fi
复杂点的脚本实现:
#!/bin/bash ### MYSQL=/data/3306/mysql LogPath=/tmp/mysql.log mysqlStatus=`mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();" >&/dev/null` if [$? -eq 0 ];then echo "db is running" exit 0 else $MYSQL start >$LogPath sleep 10 mysqlStatus=`mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();" >&/dev/null` if [ $? -ne 0 ];then while true do killall mysqld >/dev/null 2>&1 [ $? -ne 0 ] && break sleep 1 done $MYSQL start >>$LogPath && status="successful" || status="failure" mail -s "mysql startup status is $status" 12345678@qq.com < $LogPath fi fi
法4:更专业的监控数据库生成脚本解决方案
#!/bin/bash ### MYSQL=/data/3306/mysql LogPath=/tmp/mysql.log mysqlStatus=`mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();" >&/dev/null` if [$? -eq 0 ];then echo "db is running" else [ -x $MYSQL ] && $MYSQL start >$LogPath sleep 10 mysqlStatus=`mysql -uroot -p 'oldboy' -S /data/3306/mysql.sock -e "select version();" >&/dev/null` if [ $? -ne 0 ];then while true do killall mysqld >/dev/null 2>&1 [ $? -ne 0 ] && break sleep 1 done [ -x $MYSQL ] && $MYSQL start >>$LogPath && status="successful" || status="failure" mail -s "mysql startup status is $status" 1092327070@qq.com < $LogPath fi fi