03-Shell 编程之控制语句

1、if条件语句

条件表达式和if语句可以互相转换

1.1、if 三种类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 单分支语句
if [ -f /etc/hosts ]
then
echo '文件存在'
fi


# 双分支语句
if [ -f /etc/hosts ]
then
echo "文件存在"
else
echo "文件不存在"
echo "..." >>/tmp/test.log
fi


# 多分支语句
if [ -f /etc/hosts ]
then
echo " hosts文件存在"
elif [ -f /etc/host ]
then
echo " host文件存在"
fi
  • 单分支:一个条件一个结果
  • 双分支:一个条件两个结果
  • 多分支:多个条件多个结果

1.2、【练习题1】输入2个数字,比较大小(使用if语句结合条件表达式实现)

说明:3个脚本:使用直接赋值,传参,read任一种方法写3种脚本(单分支,双分支,多分支)

示例脚本一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[lepeng@centos ~]# cat if_panduan_1.sh 
#!/bin/bash
#

read -p "请输入第一个整数:" NUM1
expr 1 + $NUM1 &>/dev/null
if [ $? -eq 2 ]
then
echo "$NUM1 不是整数 "
exit 2
fi

read -p "请输入第二个整数:" NUM2
expr 1 + $NUM2 &>/dev/null
if [ $? -eq 2 ]
then
echo "$NUM2 不是整数 "
exit 2
fi

# 判断输入数值大小
if [ $NUM1 -eq $NUM2 ]
then
echo "$NUM1 = $NUM2"
exit
fi

if [ $NUM1 -gt $NUM2 ]
then
echo "$NUM1 > $NUM2"
exit
fi

if [ $NUM1 -lt $NUM2 ]
then
echo "$NUM1 < $NUM2"
fi

示例脚本二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[lepeng@centos ~]# cat if_panduan_2.sh 
#!/bin/bash
#
read -p "请输入第一个整数:" NUM1
expr 1 + $NUM1 &>/dev/null
if [ $? -eq 2 ]
then
echo "$NUM1 不是整数 "
exit 2
fi

read -p "请输入第二个整数:" NUM2
expr 1 + $NUM2 &>/dev/null
if [ $? -eq 2 ]
then
echo "$NUM2 不是整数 "
exit 2
fi

# 判断输入数值大小
if [ $NUM1 -eq $NUM2 ]
then
echo "$NUM1 = $NUM2"
exit
else
if [ $NUM1 -gt $NUM2 ]
then
echo "$NUM1 > $NUM2"
exit
else
if [ $NUM1 -lt $NUM2 ]
then
echo "$NUM1 < $NUM2"
fi
fi
fi

示例脚本三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[lepeng@centos ~]# cat if_panduan_3.sh 
#!/bin/bash
#
read -p "请输入第一个整数:" NUM1
expr 1 + $NUM1 &>/dev/null
NUM1_FH=$?
if [ $NUM1_FH -eq 2 ]
then
echo "$NUM1 不是整数 "
exit 2
else
read -p "请输入第二个整数:" NUM2
expr 1 + $NUM2 &>/dev/null
NUM2_FH=$?
if [ $NUM2_FH -eq 2 ]
then
echo "$NUM2 不是整数 "
exit 2
fi
fi

# 判断输入数值大小
if [ $NUM1 -eq $NUM2 ]
then
echo "$NUM1 = $NUM2"
exit
elif [ $NUM1 -gt $NUM2 ]
then
echo "$NUM1 > $NUM2"
exit
elif [ $NUM1 -lt $NUM2 ]
then
echo "$NUM1 < $NUM2"
fi

1.3、【练习题2】系统内存低于100M邮件报警,加入计划任务,3分钟检查一次。

对于开发程序而言,一般来说应该遵循下面的3步法则。

  1. 分析需求。明白开发需求,是完成程序的大前提,因此,分析需求至关重要,一切不以需求为主的程序开发,都是耍流氓的!

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

    1. 获取当前系统剩余内存的值(先在命令行实现)。
    2. 配置邮件报警(可采用第三方邮件服务器)。
    3. 判断取到的值是否小于100MB,如果小于100MB,就报警(采用if语句)。
    4. 编码实现Shell脚本。
    5. 加入crond定时任务,每三分钟检查一次。
  3. 编码实现。编码实现就是具体的编码及调试过程,工作中很可能需要先在测试环境下调试,调试好了,再发布到生产环境中。

第一步:先配置邮件服务,保证能够发生邮件

1
echo 'set from=mail@znix.top smtp=smtp.znix.top smtp-auth-user=mail@znix.top  smtp-auth-password=****** smtp-auth=login' >> /etc/mail.rc

发送测试邮件发送

1
echo "`date +%F_%T`" |mail -s "这是测试邮件" admin@znix.top

第二步编写检查脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[lepeng@centos ~]# cat mem_info.sh 
#!/bin/bash
#
Mem=`free -m |awk 'NR==2{print $NF}'`
host=`hostname`
Ip=`hostname -I`
Date=`date +%F_%T`
mail_file=/tmp/mail.s
dest_user=admin@znix.top

if [ $Mem -lt 100 ]
then
echo "发生时间: $Date" >$mail_file
echo "发生主机: $host 主机IP地址: $Ip " >> $mail_file
echo "内存余量: $Mem M" >> $mail_file
mail -s "【警告】内存不足了呀!" $dest_user < $mail_file
fi

第三步测试脚本(可以修改判断的值)

1
[lepeng@centos ~]# sh mem_info.sh

第四步 脚本测试成功,写入定时任务

1
2
3
[[lepeng@centos ~]# crontab -l 
# 检查内存剩余大小
*/3 * * * * /bin/sh /server/scripts/panduan/mem_info.sh &>/dev/null

至此,一个监控脚本就写好了

1.4、【练习题3】模拟编写启动nginx脚本

脚本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[lepeng@centos ~]# cat nginx.sh 
#!/bin/bash
#
. /etc/init.d/functions

StartCheck=`netstat -lntup |grep -c 80`
StartCMD='/application/nginx/sbin/nginx '
StopCMD='/application/nginx/sbin/nginx -s stop '
StatusCheck=`netstat -lntp|grep -c nginx`
ReloadCMD='/application/nginx/sbin/nginx -s reload'
CheckCMD='/application/nginx/sbin/nginx -t'
UsaGe="Usage: $0 {start|stop|status|reload|check}"

COMMAND=$1

if [ $# -ne 1 ]
then
echo $UsaGe && exit 2
fi

if [ $COMMAND = start ]
then
if [ $StartCheck == 1 ]
then
action "启动Nginx失败,端口被占用" /bin/false
else
$StartCMD
action "启动Nginx" /bin/true
fi
elif [ $COMMAND = stop ]
then
$StopCMD &>/dev/null
if [ $? -eq 0 ]
then
action "停止Nginx" /bin/true
else
action "停止Nginx" /bin/false
fi
elif [ $COMMAND = status ]
then
if [ $StatusCheck -eq 1 ]
then
echo "nginx 正在运行..."
else
echo "Nginx 未运行."
fi
elif [ $COMMAND = reload ]
then
$ReloadCMD
action "reload" /bin/true
elif [ $COMMAND = check ]
then
$CheckCMD
else
echo $UsaGe
exit 2
fi

脚本执行过程:

1.5【练习题4】Web及MySQL服务异常监测案例

用if条件语句实现对Nginx Web服务以及MySQL数据库服务是否正常进行检测,如果服务未启动,则启动相应服务。

脚本编写思路:

判断web服务器正常

  • 进程 ps -ef |grep nginx
  • 端口 netstat ss losf telnet nc nmap
  • curl 页面 返回值
  • curl check.html 的内容

判断mysql服务器正常

  • 端口 netstat ss losf telnet nc nmap
  • 进程 ps -ef |grep mysql
  • mysql 登录访问看一下
  • mysql insert一个数据 select一个数据

web服务监控脚本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[lepeng@centos ~]# cat web_check.sh 
#!/bin/bash
#
. /etc/init.d/functions

JinChen=`ps -ef |grep -c [n]ginx`
QiDong='/server/scripts/nginx.sh start'
StatuS=`curl -I -w "%{http_code}\n" -o /dev/null -s 10.0.0.180`
StatuS2=`curl -s 10.0.0.180/check.html|grep -c ok`
StartNginx='/server/scripts/nginx.sh start'

if [ $JinChen -ge 2 ]
then
if [ "$StatuS" -eq 200 ]
then
if [ "$StatuS2" -eq 1 ]
then
action "Nginx 服务运行正常" /bin/true
else
action "请检查chenk.html文件!" /bin/false
fi
else
action "请检查首页文件!" /bin/false
fi
else
action "Nginx 未正常运行!" /bin/false
$StartNginx
fi

脚本执行过程:

2、case条件结构语句

2.1、case语法结构

1
2
3
4
5
6
7
8
9
10
case "字符串变量" in 
值1)
指令1
;;
值2)
指令2
;;
值*)
指令
esac

2.2、case 值的书写方式

1
2
3
4
5
6
7
8
9
apple)
echo -e "$RED_COLOR apple $RES"
;;


# 也可以这样写,输入2种格式找同一个选项
apple|APPLE)
echo -e "$RED_COLOR apple $RES"
;;

2.3、case语句小结

  • case语句就相当于多分支的if语句。case语句的优势是更规范、易读。
  • case语句适合变量的值少,且为固定的数字或字符串集合。(1,2,3)或(start,stop,restart)。
  • 系统服务启动脚本传参的判断多用case语句,多参考rpcbind/nfs/crond脚本;菜单脚本也可以使用case

2.4、【练习题1】使用case编写一个菜单脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[lepeng@centos ~]# cat menu.sh 
#!/bin/bash
#

cat<<EOF
1. install rsync
2. install nfs
3. install mysql
4. install all
EOF

read -p '请输入你的选择:' chs

case $chs in
1)
echo install rsync success
;;
2)
echo install nfs success
;;
3)
echo install mysql success
;;
4)
echo install rsync/nfs/mysql success
;;
*)
echo "你输入有误"
esac

脚本执行过程:

2.5、if 与 case 对比

  • if 语句类似黑名单,需要把这种错误场景封堵。
  • case 语句类似白名单,只要把正确结果列完整即可。

3、for循环语句

在计算机科学中,for循环(英语:for loop)是一种编程语言的迭代陈述,能够让程式码反复的执行。

它跟其他的循环,如while循环,最大的不同,是它拥有一个循环计数器,或是循环变数。这使得for循环能够知道在迭代过程中的执行顺序。

3.1、shell中的for循环

shell中的 for 循环与在c中不同,它包含三种形式:第一种结构是列表 for 循环 ;第二种结构就是不带列表的for循环;第三种就类似于C语言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 列表for循环(常用)
#!/bin/bash
for i in 取值列表
do
循环主体/命令
done


# 不带列表for循环(示例)
#!/bin/absh
for i
do
echo "$i"
done

# 脚本执行结果
[lepeng@centos ~]# sh for2.sh http://blog.znix.top
http://blog.znix.top


# 类似C语言的风格(这种用法常在C语语言中使用)
for((exp1;exp2;exp3))
do
指令...
done

# 编写类似C语言风格脚本
for((i=0;i<=3;i++))
do
echo $i
done

3.2、不同语言的For循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Shell中的两种样式
# 样式一:
for i in 1 2 3
do
echo $i
done
# 样式二:
for i in 1 2 3;do echo $i;done


# JAVA
for(int i = 0; i < 5; i++){
//循环语句;
}


# PHP
for ($i = 0; $i < 5; $i++) {
# statements;
}

3.3、【练习题1】批量生成随机字符文件名案例

使用for循环在/clsn目录下批量创建10个html文件,其中每个文件需要包含10个随机小写字母加固定字符串clsn,名称示例如下:

1
apquvdpqbk_clsn.html  mpyogpsmwj_clsn.html  txynzwofgg_clsn.html   bmqiwhfpgv_clsn.html  udrzobsprf_clsn.html  vjxmlflawa_clsn.html  jhjdcjnjxc_clsn.html  qeztkkmewn_clsn.html jpvirsnjld_clsn.html  ruscyxwxai_clsn.html

脚本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[lepeng@centos ~]# cat make_file.sh 
#!/bin/bash
#
[ -d /clsn ] || mkdir -p /clsn
rpm -qa |grep pwgen &>/dev/null
if [ $? -eq 1 ]
then
yum install pwgen -y &>/dev/null
fi

cd /clsn &&\
for i in {1..10}
do
#File_Name=`uuidgen |tr "0-9-" "a-z"|cut -c 1-10`
File_Name2=`pwgen -1A0 10`
touch ${File_Name2}_clsn.html
done

3.4、【练习题2】批量改名特殊案例

【练习题1】中结果文件名中的clsn字符串全部改成znix(最好用for循环实现),并且将扩展名html全部改成大写。jpvirsnjld_clsn.html ===> jpvirsnjld_znix.HTML

脚本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[lepeng@centos ~]# cat rename_file.sh 
#!/bin/bash
#
cd /clsn &&\
File_name=`ls |sed -r 's#(.*)_clsn.html#\1#g'`

for i in $File_name
do
if [ -f ${i}_clsn.html ]
then
mv ${i}_clsn.html ${i}_znix.HTML
else
echo "文件修改完成."
exit
fi
done

批量改名其他方式

1
2
3
4
5
# rename 方式(最方便,专业改名)
rename txt jpg *

# 非 for 循环方式批量改名(使用sed命令进行拼接,然后交给bash执行)
ls *jpg|sed -r 's#(.*).jpg#mv & \1.mp4#'|bash

4、while循环语句

在编程语言中,while循环(英语:while loop)是一种控制流程的陈述。利用一个返回结果为布林值(Boolean)的表达式作为循环条件,当这个表达式的返回值为“真”(true)时,则反复执行循环体内的程式码;若表达式的返回值为“假”(false),则不再执行循环体内的代码,继续执行循环体下面的代码。

因为while循环在区块内代码被执行之前,先检查陈述是否成立,因此这种控制流程通常被称为是一种前测试循环(pre-test loop)。相对而言do while循环,是在循环区块执行结束之后,再去检查陈述是否成立,被称为是后测试循环。

4.1、shell中while语法

1
2
3
4
while 条件
do
命令
done

4.2、while 使用场景,多用于创建守护进程

【示例1】while实现web服务器搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[lepeng@centos ~]# vim web_view.sh 
#!/bin/bash
#
while true
do
echo "ok" | nc -l 81
done

# 客户端进行访问测试
[root@clsn html]# curl 10.0.0.180:81
ok

# 服务端显示结果:
[lepeng@centos ~]# sh web_view.sh
GET / HTTP/1.1
User-Agent: curl/7.29.0
Host: 10.0.0.180:81
Accept: */*

【示例2】:while创建定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 脚本内容:
#!/bin/bash
while true
do
uptime
sleep 0.6
done


# 脚本执行结果
[lepeng@centos ~]# sh while1.sh
15:01:52 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05
15:01:53 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05
15:01:53 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05
15:01:54 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05
15:01:55 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05
15:01:55 up 2 days, 6:02, 3 users, load average: 0.00, 0.01, 0.05

说明:

sleep 单位 秒 sleep 1 休息1秒

usleep 单位 微秒 usleep 1000000 休息1s

1微秒等于百万分之一秒(10的负6次方秒)

【示例3】:计算1-100的和

方法一 (bc命令实现)

1
echo `seq -s + 1 100`|bc

方法二(while循环方法)

1
2
3
4
5
6
7
8
9
10
11
[lepeng@centos ~]# cat jishan.sh 
#!/bin/bash
#
i=1

while [ "$i" -le 100 ]
do
((b=b+i))
((i++))
done
echo $b

示例3:实现类似手机通讯计费功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[lepeng@centos ~]# cat while/shouji.sh 
#!/bin/bash
#
sum=1000
i=15

while [ $sum -ge 15 ]
do
cat<<EOF
=================
1.发短信
2.查余额
3.账户充值
4.退出
=================
EOF
read -p "你要做什么呢?" Some
case "$Some" in
1)
sum=$((sum-i))
read -p "请输入发送短信的内容:"
read -p "请输入收信人:"
sleep 0.3
echo "发送成功."
echo "您当前余额为$sum"
;;
2)
echo "您当前余额为$sum"
;;
3)
read -p "请输入你要充值的金额:" ChongZhi
sum=$((sum+ChongZhi))
echo "充值成功,当前余额为$sum"
;;
4)
exit
;;
*)
echo "输入有误!"
exit 2
esac
done

echo "余额不足,请及时充值!"

5、break continue exit return

条件与循环控制及程序返回值命令表

命令 说明
break n 如果省略n,则表示跳出整个循环,n表示跳出循环的层数
continue n 如果省略n,则表示跳过本次循环,忽略本次循环的剩余代码,进人循环的下一次循环;n表示退到第n层继续循环
exit n 退出当前Shell程序,n为上一次程序执行的状态返回值。n也可以省略,在下一个Shell里可通过”$?”接收exit n的n值
return n 用于在函数里作为函数的返回值,以判断函数执行是否正确。在下一个Shell里可通过”$?”接收exit n的n值

简单来说即:

  • break 跳出循环
  • continue 跳出本次循环
  • exit 退出脚本
  • return 与 exit 相同,在函数中使用

5.1、break命令说明

1
2
3
4
5
6
7
break: break [n]
退出 forwhile、或 until 循环

退出一个 FORWHILEUNTIL 循环。如果指定了N,则打破N重循环

退出状态:
退出状态为 0 除非 N 不大于或等于 1

5.2、continue命令说明

1
2
3
4
5
6
7
8
continue: continue [n]
继续 forwhile、或 until 循环。

继续当前 FORWHILEUNTIL 循环的下一步。
如果指定了 N, 则继续当前的第 N 重循环。

退出状态:
退出状态为 0 除非 N 不大于或等于1

5.3、exit命令说明

1
2
3
4
5
exit: exit [n]
退出shell。

以状态 N 退出 shell。 如果 N 被省略,则退出状态
为最后一个执行的命令的退出状态。

5.4、return 命令说明

1
2
3
4
5
6
7
8
9
return: return [n]
从一个 shell 函数返回。

使一个函数或者被引用的脚本以指定的返回值 N 退出。
如果 N 被省略,则返回状态就是
函数或脚本中的最后一个执行的命令的状态。

退出状态:
返回 N,或者如果 shell 不在执行一个函数或引用脚本时,失败。

03-Shell 编程之控制语句
https://flepeng.github.io/021-Shell-03-Shell-编程之控制语句/
作者
Lepeng
发布于
2016年1月1日
许可协议