shell脚本编写思路和实例讲解
前言
常听见身边有很多学习shell脚本的朋友抱怨shell脚本不好写,好不容易写出来的脚本一直报错,符号空格又太多,错了一个就无法运行还不好排查错误。
客观讲shell脚本的简洁程度确实有些堪忧,但是在Linux运维工作中shell脚本运用得非常广,在有些场合用shell会非常有效率,所以shell脚本编写是必须掌握的技能之一,我们还不能舍弃。
方法要点
言归正传,接下来我将分享我在编写shell脚本的时候学到的经验和写shell脚本的方法。
1.理清思路:在我们想实现一个功能需要写脚本的时候是切忌不要想到就写,避免需要什么功能就先写个什么命令再去完善,这种写法常常会出现在结构上很难调整的问题。在我们开始写脚本之前一定要事先理清楚要怎么去实现这个功能,包括用什么命令和命令的先后顺序。
2.全局观:当我们理清脚本的思路之后,我们这个时候也不能马上就开始写,这个时候我们需要在脑海中思考每一个步骤中可能出现的情况,当出现各种情况后如何应对。在shell脚本中有可能我们要实现一个看似简单的功能会需要有多个条件判断,因为出现在我们当前系统上的情况只是在众多情况中的一种,我们要实现让脚本在任何机器上都能正常运行就必须有“全局观”。通俗讲就是一个排除bug的过程。
3.心细如丝:正如大家感慨的一样,shell脚本有非常多的引号括号空格,每一种意义不一样,多个空格少个空格意义又不一样,写个几十行的shell脚本能一次性写完就无错运行还是有一些难度的。我的在学习shell脚本一开始就强行给自己养成一个习惯,每写完一行就检查一次各种符号括号,刚开始速度非常慢,但是写得多了速度就越来越快而且出错率也越来越低,当养成习惯以后再去写那些符号就成了习惯没有刚开始那么痛苦了。
4.好记性不如烂键盘:有一句不好听的话虽然不好听,但是能提醒到一些正在学习shell脚本的朋友说出来也挺好。当你在抱怨shell脚本如何难写如何易出错的时候那么你总共写过多少行shell?你敲过多少次键盘?当你无数次练习,把键盘都敲到字都掉完了之后还会说这句话吗?对!你还会说这句话,但已经不是抱怨而是评价。
实例
编写脚本checkdisk.sh,运行效果:检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满
看到题目后先审题,然后按照我们上面的方法来。
第一步,我们先把要实现的功能理清。
要查看磁盘分区和inode的使用情况那么会用到 df 和df -i 命令
提取出df命令中使用率的部分会用到grep用到cut以及head还有tail等这些对文本处理的命令
要比较数值那么可能会用到中括号和数值比较
最后发广播要用到wall命令
因为磁盘分区并不是只有一个,或许还会用到if条件判断以及循环。
这个时候我们的思路可以是这样:
1.先用grep等文本处理命令取出df中的百分比值
2.用中括号判断这些值这些值是否大于80
3.将大于百分之80的值对应的分区用wall进行广播
第二步,思考每一步会出现的问题和情况然后应对。
在上一步中我们已经把主线理清,既然线路已经规划了那么就开始铺路吧。
1.要取出df命令中的百分比值我们首先需要观察百分比值在df命中是出现在哪个位置,然后用正则表达式匹配,之后用cut等命令取出,但是分区有很多个而且还要判断inode的使用情况所以我们还要搞清楚一共有多少个分区,那么这里会用到几个变量:代表磁盘空间分区个数的变量、代表磁盘inode分区个数的变量、代表被取出的磁盘分区路径的变量、代表被取出的百分比数值的变量。
2.因为包括inode的话磁盘分区要比较的就很多,如果挨个比较的话就会大大增加脚本的行数,写起来非常累,这里就可以考虑利用分区的个数变量利用循环语句来比较每个分区使用量是否达到80%以上以及输出比较结果,而考虑到还有inode则可以用if elif来判断。
3.既然路也铺好了,那就上路吧。
下面我会将我写的脚本贴在这里,可能看起来会有一些长,但是其实只要认真阅读了我上面所有的阐述就会觉得其实非常简单。
注意:在脚本中出现的正则表达式我将不再细讲,如果想了解的朋友去我上一篇博客中查阅一下。脚本中如cut这些命令具体功能我不做细讲,有兴趣的朋友可以在网上查询相关资料。
#!/bin/bash
#author:Driver_C
#mail:740560896@qq.com
#time:2017-06-11
#filename:checkdisk.sh //以上为注释段
linenum=`df | egrep "\<[[:digit:]]{1,3}%.*" | wc -l`
#将用正则表达式取到的磁盘分区个数赋值给linenum
test1=`df | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n1 | cut -d' ' -f1`
#经过一系列的文本处理取出磁盘分区使用量最大的分区的数值赋给test1,这个变量在决定循环是否进行的关键
test2=`df -i | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n1 | cut -d' ' -f1`
#如同上一条命令,df选项多了-i,意义在取出inode使用量最大的分区使用量数值赋给test2,同样也是循环是否进行的关键
num=1
#这个变量在整个判断过程中非常重要,这个变量用于给while循环中判断当前操作行,判断之后加1以实现循环的
if [ "$test1" -ge "80" ];then
#这个if的意义在于判断之前磁盘分区使用率最大的分区是否大于或等于80%,是就执行循环依次判断每个分区
while [ "$num" -le "$linenum" ]
#这里就用到了上面的num和linenum变量,意义在于当num小于或等于磁盘分区行数的时候才执行下面的命令,相当于只要刚才的if判断有分区使用量大于80%就会把所有分区都放进循环里比较,因为有可能使用量大于80%并不只有一个分区。
do
testnum1=`df | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n"$num" | tail -n1 | head -n1 | cut -d' ' -f1`
#这一段命令的功能是用于提取出分区使用量的数值用于后面的比较,这里注意一下我在命令中num变量的用法,num需要带进命令中才能实现不同行的提取
testname1=`df | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n"$num" | tail -n1 | head -n1 | cut -d' ' -f2`
#这一段主要功能是提取分区路径,意义在于上一行命令提取出来的数值如果大于80%,在广播磁盘分区不足的时候就能给出到底是哪一个分区快要满了
[ "$testnum1" -ge "80" ] && wall "$testname1"' will be full'
#这一段就是比较数值的和输出的部分,命令很简单比较好理解,要注意的是用的变量要看清
num=$[num+1]
#当循环一次之后num加上1再次循环的时候就能匹配下一行的信息了
done
num=1
#num变量是否重新赋值为1直接决定了下面的判断是否能正常运行,因为上面的循环结束后num的值为7,如果不重新赋值为1那后面的命令相当于没写。
#这一部分的结构和上面是一样的,我就不再每条都做解释,这一段的主要功能是判断inode的用量。
elif [ "$test2" -ge "80" ]
then
while [ "$num" -le "$linenum" ]
do
testnum2=`df -i | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n"$num" | tail -n1 | head -n1 | cut -d' ' -f1`
testname2=`df -i | egrep -o "\<[[:digit:]]{1,3}%.*" | tr '%' ' ' | tr -s ' ' |sort -nr | head -n"$num" | tail -n1 | head -n1 | cut -d' ' -f2`
[ "$testnum2" -ge "80" ] && wall "$testname2"' Inode will be full'
num=$[num+1]
done
else
echo "All filesystems are safe"
#这里非常好理解,如果上面的没有一个匹配上就直接输出这一段信息。
fi
#养成好习惯,用过的变量结束就回收。
unset linenum
unset test1
unset test2
unset num
unset testnum1
unset testname1
unset testnum2
unset testname2
exit
脚本内容就是以上部分,其实按照我之前的思路,这个脚本虽然行数不少,但是并不难以理解,写脚本没有想象的那么难,难的是下不去手。本文这个题目对初学者来说是个不错的练习。
试验
看到这里有朋友会说了,上面稀里哗啦说这么多到底能不能用都不知道毕竟“Linux is interesting”我们需要去玩玩才知道。
试验系统:CentOS 7.3 1611
[root@centos7 ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 104806400 5397480 99408920 6% /
devtmpfs 485308 0 485308 0% /dev
tmpfs 499980 4 499976 1% /dev/shm
tmpfs 499980 7196 492784 2% /run
tmpfs 499980 0 499980 0% /sys/fs/cgroup
/dev/sda3 52403200 32944 52370256 1% /app
/dev/sda1 1038336 172116 866220 17% /boot
tmpfs 100000 8 99992 1% /run/user/0
/dev/sr0 8086368 8086368 0 100% /run/media/root/CentOS 7 x86_64
[root@centos7 ~]#
可以看到我因为在系统上挂了一张光盘,所以有一个分区是100%,运行一下脚本试试能不能测出来。
[root@centos7 ~]# checkdisk02.sh
Broadcast message from root@centos7.com (pts/0) (Sun Jun 11 15:44:12 2017):
/run/media/root/CentOS will be full
[root@centos7 ~]#
完美呈现出大于80%的分区名。
那么我们现在再把一个分区使用率增加到80%以上看看是否能都显示出来。
[root@centos7 ~]# dd if=/dev/zero of=/boot/file bs=1M count=800
#这里我们可以用dd命令在/boot目录下写入一个文件把/boot分区撑过80%
800+0 records in
800+0 records out
838860800 bytes (839 MB) copied, 2.30907 s, 363 MB/s
[root@centos7 ~]# checkdisk02.sh
#执行脚本
Broadcast message from root@centos7.com (pts/0) (Sun Jun 11 15:46:43 2017):
/run/media/root/CentOS will be full
Broadcast message from root@centos7.com (pts/0) (Sun Jun 11 15:46:43 2017):
/boot will be full
[root@centos7 ~]#
当我们把另外一个分区也撑过80%后依然完美呈现。脚本运行正常。
结语
其实在我们的学习过程中如果遇到感觉很吃力的东西并不是说自己智力有限了自己能力有限了,那只不过是给键盘敲少了找借口,多敲多练即使理解不了也学会怎么用了。