第十四章 SHELL 脚本

         终于到shell 脚本这章了,在之前笔者卖了好多关子说shell脚本怎么怎么重要,确实shell脚本在linux系统管理员的运维工做中很是很是重要。下面笔者就带你正式进入shell脚本的世界吧。linux

         到如今为止,你明白什么是shell脚本吗?若是明白最好了,不明白也没有关系,相信随着学习的深刻你就会愈来愈了解到底什么是shell脚本。首先它是一个脚本,并不能做为正式的编程语言。由于是跑在linux的shell中,因此叫shell脚本。说白了,shell脚本就是一些命令的集合。举个例子,我想实现这样的操做:1)进入到/tmp/目录;2)列出当前目录中全部的文件名;3)把全部当前的文件拷贝到/root/目录下;4)删除当前目录下全部的文件。简单的4步在shell窗口中须要你敲4次命令,按4次回车。这样是否是很麻烦?固然这4步操做很是简单,若是是更加复杂的命令设置须要几十次操做呢?那样的话一次一次敲键盘会很麻烦。因此不妨把全部的操做都记录到一个文档中,而后去调用文档中的命令,这样一步操做就能够完成。其实这个文档呢就是shell脚本了,只是这个shell脚本有它特殊的格式。web

         Shell脚本能帮助咱们很方便的去管理服务器,由于咱们能够指定一个任务计划定时去执行某一个shell脚本实现咱们想要需求。这对于linux系统管理员来讲是一件很是值得自豪的事情。如今的139邮箱很好用,发邮件的同时还能够发一条邮件通知的短信给用户,利用这点,咱们就能够在咱们的linux服务器上部署监控的shell脚本,好比网卡流量有异常了或者服务器web服务器中止了就能够发一封邮件给管理员,同时发送给管理员一个报警短信这样可让咱们及时的知道服务器出问题了。shell

         有一个问题须要约定一下,凡是自定义的脚本建议放到/usr/local/sbin/目录下,这样作的目的是,一来能够更好的管理文档;二来之后接管你的管理员都知道自定义脚本放在哪里,方便维护。编程

shell脚本的基本结构以及如何执行vim

Shell脚本一般都是以.sh 为后缀名的,这个并非说不带.sh这个脚本就不能执行,只是你们的一个习惯而已。因此,之后你发现了.sh为后缀的文件那么它必定会是一个shell脚本了。test.sh中第一行必定是 “#! /bin/bash” 它表明的意思是,该文件使用的是bash语法。若是不设置该行,那么你的shell脚本就不能被执行。’#’表示注释,在前面讲过的。后面跟一些该脚本的相关注释内容以及做者和建立日期或者版本等等。固然这些注释并不是必须的,若是你懒的很,能够省略掉,可是笔者不建议省略。由于随着你工做时间的增长,你写的shell脚本也会愈来愈多,若是有一天你回头查看你写的某个脚本时,颇有可能忘记该脚本是用来干什么的以及何时写的。因此写上注释是有必要的。另外系统管理员并不是你一个,若是是其余管理员查看你的脚本,他看不懂岂不是很郁闷。该脚本再往下面则为要运行的命令了。bash

Shell脚本的执行很简单,直接”sh  filename “ 便可,另外你还能够这样执行服务器

默认咱们用vim编辑的文档是不带有执行权限的,因此须要加一个执行权限,那样就能够直接使用’./filename’ 执行这个脚本了。另外使用sh命令去执行一个shell脚本的时候是能够加-x选项来查看这个脚本执行过程的,这样有利于咱们调试这个脚本哪里出了问题。less

该shell脚本中用到了’date’这个命令,它的做用就是用来打印当前系统的时间。其实在shell脚本中date使用率很是高。有几个选项笔者经常在shell脚本中用到:运维

%Y表示年,%m表示月,%d表示日期,%H表示小时,%M表示分钟,%S表示秒编程语言

注意%y和%Y的区别。

-d 选项也是常常要用到的,它能够打印n天前或者n天后的日期,固然也能够打印n个月/年前或者后的日期。

另外星期几也是经常使用的

【shell脚本中的变量】

         在shell脚本中使用变量显得咱们的脚本更加专业更像是一门语言,开个玩笑,变量的做用固然不是为了专业。若是你写了一个长达1000行的shell脚本,而且脚本中出现了某一个命令或者路径几百次。忽然你以为路径不对想换一下,那岂不是要更改几百次?你当然可使用批量替换的命令,可是也是很麻烦,而且脚本显得臃肿了不少。变量的做用就是用来解决这个问题的。

在test2.sh中使用到了反引号,你是否还记得它的做用?’d’和’d1’在脚本中做为变量出现,定义变量的格式为 “变量名=变量的值”。当在脚本中引用变量时须要加上’$’符号,这跟前面讲的在shell中自定义变量是一致的。下面看看脚本执行结果吧。

下面咱们用shell计算两个数的和。

数学计算要用’[ ]’括起来而且外头要带一个’$’。脚本结果为:

Shell脚本还能够和用户交互。

这就用到了read命令了,它能够从标准输入得到变量的值,后跟变量名。”read  x”表示x变量的值须要用户经过键盘输入获得。脚本执行过程以下:

咱们不妨加上-x选项再来看看这个执行过程:

在test4.sh中还有更加简洁的方式。


read -p 选项相似echo的做用。执行以下:

         你有没有用过这样的命令”/etc/init.d/iptables restart “ 前面的/etc/init.d/iptables 文件其实就是一个shell脚本,为何后面能够跟一个”restart”? 这里就涉及到了shell脚本的预设变量。实际上,shell脚本在执行的时候后边是能够跟变量的,并且还能够跟多个。不妨笔者写一个脚本,你就会明白了。


执行过程以下:

在脚本中,你会不会奇怪,哪里来的$1和$2,这其实就是shell脚本的预设变量,其中$1的值就是在执行的时候输入的1,而$2的值就是执行的时候输入的$2,固然一个shell脚本的预设变量是没有限制的,这回你明白了吧。另外还有一个$0,不过它表明的是脚本自己的名字。不妨把脚本修改一下。


执行结果想必你也猜到了吧。

【shell脚本中的逻辑判断】

         若是你学过C或者其余语言,相信你不会对if 陌生,在shell脚本中咱们一样可使用if逻辑判断。在shell中if判断的基本语法为:

1)不带else

         if  判断语句; then

                   command

         fi

在if1.sh中出现了 ((a<60))这样的形式,这是shell脚本中特有的格式,用一个小括号或者不用都会报错,请记住这个格式,便可。执行结果为:

2)带有else

if  判断语句  ; then

                   command

else

                   command

fi

执行结果为:

3)带有elif

if  判断语句一  ; then

                   command

elif  判断语句二; then

                   command

         else

                   command

fi

这里的 && 表示“而且”的意思,固然你也可使用 || 表示“或者”,执行结果:

以上只是简单的介绍了if语句的结构。在判断数值大小除了能够用”(( ))”的形式外,还可使用”[ ]”。可是就不能使用>, < , = 这样的符号了,要使用 -lt (小于),-gt (大于),-le (小于等于),-ge(大于等于),-eq (等于),-ne (不等于)。

再看看if中使用 && 和 ||的状况。

shell 脚本中if还常常判断关于档案属性,好比判断是普通文件仍是目录,判断文件是否有读写执行权限等。经常使用的也就几个选项:

-e :判断文件或目录是否存在

-d :判断是否是目录,并是否存在

-f :判断是不是普通文件,并存在

-r :判断文档是否有读权限

-w :判断是否有写权限

-x :判断是否可执行

使用if判断时,具体格式为: if [ -e filename ] ; then

在shell 脚本中,除了用if来判断逻辑外,还有一种经常使用的方式,那就是case了。具体格式为:

         case  变量  in

         value1)

                   command

                   ;;

         value2)

                   command

                   ;;

         value3)

                   command

                   ;;

         *)

                   command

                   ;;

         esac

上面的结构中,不限制value的个数,*则表明除了上面的value外的其余值。下面笔者写一个判断输入数值是奇数或者偶数的脚本。

$a 的值或为1或为0,执行结果为:

也能够看一下执行过程:

case脚本经常使用于编写系统服务的启动脚本,例如/etc/init.d/iptables中就用到了,你不妨去查看一下。

【shell脚本中的循环】

         Shell脚本中也算是一门简易的编程语言了,固然循环是不能缺乏的。经常使用到的循环有for循环和while循环。下面就分别介绍一下两种循环的结构。

脚本中的seq 1 5 表示从1到5的一个序列。你能够直接运行这个命令试下。脚本执行结果为:

经过这个脚本就能够看到for循环的基本结构 :

for 变量名 in 循环的条件; do

         command

done

循环的条件那一部分也能够写成这样的形式,中间用空格隔开便可。你也能够试试,for i in `ls`; do echo $i; done 和  for i in `cat test.txt`; do echo $i; done

再来看看这个while循环,基本格式为:

         while  条件; do

                   command

         done

脚本的执行结果为:

另外你能够把循环条件忽略掉,笔者经常这样写监控脚本。

while :; do

command

done

【shell脚本中的函数】

         若是你学过开发,确定知道函数的做用。若是你是刚刚接触到这个概念的话,也没有关系,其实很好理解的。函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字便可。有时候脚本中的某段代老是重复使用,若是写成函数,每次用到时直接用函数名代替便可,这样就节省了时间还节省了空间。

fun.sh 中的sum() 为自定义的函数,在shell脚本中要用

function 函数名() {

command

}

这样的格式去定义函数。

上个脚本执行过程以下:

有一点笔者要提醒你一下,在shell脚本中,函数必定要写在最前面,不能出如今中间或者最后,由于函数是要被调用的,若是尚未出现就被调用,确定是会出错的。

         Shell脚本大致上就介绍这么多了,笔者所举的例子都是最基础的,因此即便你把全部例子彻底掌握也不表明你的shell脚本编写能力有多么好。因此剩下的日子里你尽可能要多练习,多写脚本,你写的脚本越多,你的能力就越强。但愿你可以找专门介绍shell脚本的书籍深刻的去研究一下它。随后笔者将给你留几个shell脚本的练习题,你最好不要偷懒。

1. 编写shell脚本,计算1-100的和;

2. 编写shell脚本,要求输入一个数字,而后计算出从1到输入数字的和,要求,若是输入的数字小于1,则从新输入,直到输入正确的数字为止;

3. 编写shell脚本,把/root/目录下的全部目录(只须要一级)拷贝到/tmp/目录下;

4. 编写shell脚本,批量创建用户user_00, user_01, … ,user_100而且全部用户同属于users组;

5. 编写shell脚本,截取文件test.log中包含关键词’abc’的行中的第一列(假设分隔符为”:”),而后把截取的数字排序(假设第一列为数字),而后打印出重复次数超过10次的列;

6. 编写shell脚本,判断输入的IP是否正确(IP的规则是,n1.n2.n3.n4,其中1<n1<255, 0<n2<255, 0<n3<255, 0<n4<255)。

如下为练习题答案:

1. #! /bin/bash

sum=0

for i in `seq 1 100`; do

        sum=$[$i+$sum]

done

echo $sum

2. #! /bin/bash

n=0

while [ $n -lt "1" ]; do

        read -p "Please input a number, it must greater than "1":" n

done

 

sum=0

for i in `seq 1 $n`; do

        sum=$[$i+$sum]

done

echo $sum

 

3. #! /bin/bash

for f in `ls /root/`; do

        if [ -d $f ] ; then

                cp -r $f /tmp/

        fi

done

 

4. #! /bin/bash

groupadd users

for i in `seq 0 9`; do

        useradd -g users user_0$i

done

 

for j in `seq 10 100`; do

        useradd -g users user_$j

done

 

5. #! /bin/bash

awk -F':' '$0~/abc/ {print $1}' test.log >/tmp/n.txt

sort -n n.txt |uniq -c |sort -n >/tmp/n2.txt

awk '$1>10 {print $2}' /tmp/n2.txt

 

6. #! /bin/bash

checkip() {

        if echo $1 |egrep -q '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' ; then

                a=`echo $1 | awk -F. '{print $1}'`

                b=`echo $1 | awk -F. '{print $2}'`

                c=`echo $1 | awk -F. '{print $3}'`

                d=`echo $1 | awk -F. '{print $4}'`

 

                for n in $a $b $c $d; do

                        if [ $n -ge 255 ] || [ $n -le 0 ]; then

                                echo "the number of the IP should less than 255 and greate than 0"

                                return 2

                        fi

                done

        else

                echo "The IP you input is something wrong, the format is like 192.168.100.1"

                return 1

        fi

}

 

rs=1

while [ $rs -gt 0 ]; do

read -p  "Please input the ip:" ip

checkip $ip

rs=`echo $?`

done

echo "The IP is right!"