20.1 shell 脚本介绍

  • shell是一种脚本语言

  • 可以使用逻辑判断、循环等语法

  • 可以自定义函数

  • shell是系统命令的集合

  • shell脚本可以实现自动化运维,能大大增加我们的运维效率


20.2 shell 脚本结构和执行

  • 开头需要加 #!/bin/bash,不加开头在本机上可以运行,换台机器就不一定能执行。在行首文件头处指定接下来要运行的命令是通过哪一个解释器进行操作的。

  • 以#开头的行作为解释说明,脚本的名字以 .sh 结尾,用于区分这是一个 shell 脚本

执行方法有两种:

1、/bin/bash 实际上就是 /bin/sh

[root@arslinux-01 ~]# ll /bin/sh
lrwxrwxrwx. 1 root root 4 3月  14 05:51 /bin/sh -> bash
[root@arslinux-01 ~]# ll /bin/bash
-rwxr-xr-x. 1 root root 964608 10月 31 2018 /bin/bash

2、给文件执行权限,chmod a+x 1.sh 才可以 ./1.sh

[root@arslinux-01 shell]# ./1.sh
-bash: ./1.sh: 权限不够
[root@arslinux-01 shell]# chmod a+x 1.sh
[root@arslinux-01 shell]# ./1.sh
123
22:43:46 up  2:39,  1 user,  load average: 0.10, 0.09, 0.13
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.194.1    20:08    2.00s  2:44   0.00s /bin/bash ./1.sh
1.sh

3、如果没有权限,可以 /bin/bash 1.sh

[root@arslinux-01 shell]# /bin/bash 1.sh
[root@arslinux-01shell]# /bin/bash 1.sh
123
22:48:59 up  2:44,  1 user,  load average: 0.01, 0.05, 0.11
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.194.1    20:08    3.00s  2:51   0.00s /bin/bash 1.sh
1.sh

查看脚本执行过程: bash -x 1.sh

查看脚本是否语法错误:  bash -n 1.sh


20.3 date 命令用法

[root@arslinux-01 ~]# date
2019年 06月 15日 星期六 23:11:02 CST

date  +%Y-%m-%d, date +%y-%m-%d    年月日

[root@arslinux-01 ~]# date +%Y-%m-%d
2019-06-15
[root@arslinux-01 ~]# date +%y-%m-%d
19-06-15
[root@arslinux-01 ~]# date +%Y        //年
2019
[root@arslinux-01 ~]# date +%M        //分钟
14
[root@arslinux-01 ~]# date +%D
06/15/19
[root@arslinux-01 ~]# date +%F
2019-06-15
[root@arslinux-01 ~]# date +%S        //秒
09
[root@arslinux-01 ~]# date +%s        //时间戳,距离 1970.01.01.00:00:00 多少秒
1560611775

date +%H:%M:%S = date +%T    时间

[root@arslinux-01 ~]# date +%H:%M:%S
23:18:23
[root@arslinux-01 ~]# date +%T
23:18:29
[root@arslinux-01 ~]# date +%h
6月
[root@arslinux-01 ~]# date +%m
06

date +%s     时间戳

[root@arslinux-01 ~]# date +%s
1560612094
距离 1970.01.01.00:00:00 多少秒

date +%w, date +%W    星期

[root@arslinux-01 ~]# date +%w        //星期几
6
[root@arslinux-01 ~]# date +%W        //几年的第几周
23

cal    日历形式查看日期

[root@arslinux-01 ~]# cal
六月 2019
日 一 二 三 四 五 六
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

date -d "-1 day"    一天前

[root@arslinux-01 ~]# date -d "-1 day"
2019年 06月 14日 星期五 23:26:15 CST
[root@arslinux-01 ~]# date -d "-1 day" +%F
2019-06-14

date -d "+1day"     一天后

[root@arslinux-01 ~]# date -d "+1 day" +%F
2019-06-16

date -d "-1 month"    一月前

[root@arslinux-01 ~]# date -d "-1 month" +%F
2019-05-15
[root@arslinux-01 ~]# date -d "-1 year" +%F
2018-06-15
[root@arslinux-01 ~]# date -d "-1 week" +%F
2019-06-08

date -d "-1 min"     一分钟前

[root@arslinux-01 ~]# date -d "-1 min" +%T
23:28:02

date -d @1504620492    根据时间戳换算成日期

[root@arslinux-01 ~]# date -d @1504620492
2017年 09月 05日 星期二 22:08:12 CST

date +%s -d "2019-09-09 09:09:09"    把具体的日期换算成时间戳

[root@arslinux-01 ~]# date +%s -d "2019-09-09 09:09:09"
1567991349


20.4 shell 脚本中的变量

  • 当脚本中使用某个字符串较频繁并且字符串长度很长时就应该使用变量代替

  • 使用条件语句时,常使用变量    if [ $a -gt 1 ]; then ... ; fi

  • 引用某个命令的结果时,用变量替代   n=`wc -l 1.txt`

  • 写和用户交互的脚本时,变量也是必不可少的  read -p "Input a number: " n; echo $n   如果没写这个n,可以直接使用$REPLY

  • 内置变量 $0, $1, $2…    $0表示脚本本身,$1 第一个参数,$2 第二个 ....       $#表示参数个数

  • 数学运算a=1;b=2; c=$(($a+$b))或者$[$a+$b]


20.5 shell 脚本中的逻辑判断

格式1:if 条件;then 语句;fi

[root@arslinux-01 ~]# vim 01.sh
#/bin/bash
a=5
if [ $a -gt 3 ]
then
    echo ok
fi

格式2:if 条件;then 语句;else 语句;fi

[root@arslinux-01 shell]# vim 02.sh

#/bin/bash
a=1
if [ $a -gt 3 ]
then
    echo ok
else
    echo not ok
fi
[root@arslinux-01 shell]# sh -x 02.sh
+ a=1
+ '[' 1 -gt 3 ']'
+ echo not ok
not ok

格式3:if ...;then ...;elif ...;then ...;else ...;fi

[root@arslinux-01 shell]# vim 03.sh
#/bin/bash
a=3
if [ $a -gt 4 ]
then
    echo ">4"
elif [ $a -lt 6 ]
then
    echo "<6 && >1"
else
    echo not ok
fi
[root@arslinux-01 shell]# sh -x  03.sh
+ a=3
+ '[' 3 -gt 4 ']'
+ '[' 3 -lt 6 ']'
+ echo '<6 && >1'
<6 && >1
[root@arslinux-01 shell]# vim 03.sh
#/bin/bash
a=5
if [ $a -lt 4 ]
then
    echo "<4"
elif [ $a -gt 6 ]
then
    echo ">6"
else
    echo not ok
fi
[root@arslinux-01 shell]# sh -x  03.sh
+ a=5
+ '[' 5 -lt 4 ']'
+ '[' 5 -gt 6 ']'
+ echo not ok
not ok
  • 逻辑判断表达式:

if [ $a -gt $b ]; -gt (>); 大于

if [ $a -lt 5 ]; -lt(<); 小于

if [ $b -eq 10 ]等 -eq(==); 等于

-le(<=);-ge(>=); -ne(!=) 注意到处都是空格

·如果非要用 > < ,可以用(( ))

[root@localhost shell]# if (($a>1));then echo ok;fi
ok

·可以使用 && || 结合多个条件

if [ $a -gt 5 ] && [ $a -lt 10 ]; then 并且

if [ $b -gt 5 ] || [ $b -lt 3 ]; then 或者


20.6 文件目录属性判断

·[ -f file ] 判断是否是普通文件,且存在

·[ -d file ] 判断是否是目录,且存在

·[ -e file ] 判断文件或目录是否存在

·[ -r file ] 判断文件是否可读

·[ -w file ] 判断文件是否可写

·[ -x file ] 判断文件是否可执行


[ -f file ] 判断是否是普通文件,且存在

[root@arslinux-01 shell]# vim 04.sh
#/bin/bash
f="/tmp/arslinux"
if [ -f $f ]
then
    echo $f exist.
else
    touch $f
fi
[root@arslinux-01 shell]# sh -x 04.sh
+ f=/tmp/arslinux
+ '[' -f /tmp/arslinux ']'
+ touch /tmp/arslinux
[root@arslinux-01 shell]# sh -x 04.sh
+ f=/tmp/arslinux
+ '[' -f /tmp/arslinux ']'
+ echo /tmp/arslinux exist.
/tmp/arslinux exist.

[ -d file ] 判断是否是目录,且存在

[root@arslinux-01 shell]# vim file2.sh
#/bin/bash
f="/tmp/arslinux"
if [ -d $f ]
then
    echo $f exist.
else
    touch $f
fi
[root@arslinux-01 shell]# sh -x file2.sh
+ f=/tmp/arslinux
+ '[' -d /tmp/arslinux ']'
+ touch /tmp/arslinux

[ -e file ] 判断文件或目录是否存在

[root@arslinux-01 shell]# vim file3.sh
#/bin/bash
f="/tmp/arslinux"
if [ -e $f ]
then
    echo $f exist.
else
    touch $f
fi
[root@arslinux-01 shell]# sh -x file3.sh
+ f=/tmp/arslinux
+ '[' -e /tmp/arslinux ']'
+ echo /tmp/arslinux exist.
/tmp/arslinux exist.

[ -r file ] 判断文件是否可读

[root@arslinux-01 shell]# vim file4.sh
#/bin/bash
f="/tmp/arslinux"
if [ -r $f ]
then
    echo $f readable.
fi
[root@arslinux-01 shell]# sh -x file4.sh
+ f=/tmp/arslinux
+ '[' -r /tmp/arslinux ']'
+ echo /tmp/arslinux readable.
/tmp/arslinux readable.

[ -w file ] 判断文件是否可写

[root@arslinux-01 shell]# vim file4.sh
#/bin/bash
f="/tmp/arslinux"
if [ -w $f ]
then
    echo $f writeable.
fi
[root@arslinux-01 shell]# sh -x file4.sh
+ f=/tmp/arslinux
+ '[' -w /tmp/arslinux ']'
+ echo /tmp/arslinux writeable.
/tmp/arslinux writeable.

[ -x file ] 判断文件是否可执行

[root@arslinux-01 shell]# vim file4.sh

#/bin/bash
f="/tmp/arslinux"
if [ -x $f ]
then
    echo $f exeable.
fi
[root@arslinux-01 shell]# sh -x file4.sh
+ f=/tmp/arslinux
+ '[' -x /tmp/arslinux ']'

简单写法

#!/bin/bash
f="/tmp/alex"
if [ -f $f ]
then
rm -f $f
fi

可以不用 if 判断写成

#!/bin/bash
f="/tmp/alex"
[ -f $f ] && rm -f $f
#!/bin/bash
f="/tmp/alex"
if [ -f $f ]
then
    rm -f $f
else
    touch $f
fi

可以反写为

#!/bin/bash
f="/tmp/alex"
if [ ! -f $f ]
then
    touch $f
fi

可以不用 if 判断,写成

#!/bin/bash
f="/tmp/alex"
[ -f $f ] || touch $f


20.7 if 特殊用法

  • if [ -z "$a" ]  这个表示当变量 a 的值为空时会怎么样

#!/bin/bash
n=`wc -l /tmp/bababa`
if [ -z "$n" ]
then
    echo error
else
    if [ $n -gt 100]
    then
        echo ok
    fi
fi

可以优化为:

#!/bin/bash
n=`wc -l /tmp/bababa`
if [ -z "$n" ]
then
    echo error
exit
elif [ $n -gt 100]
then
    echo ok
fi

可以再优化:

#!/bin/bash
if [ -f /tmp/babala ]
then
    echo "/tmp/babala isn't exist."
    exit
fi
n=`wc -l /tmp/bababa`
if [ -z "$n" ]
then
    echo error
    exit
elif [ $n -gt 100]
then
    echo ok
fi
  • if [ -n "$a" ] 表示当变量 a 的值不为空

[root@arslinux-01 shell]# if [ -n 01.sh ];then echo ok;fi
ok
[root@arslinux-01 shell]# if [ -n "$b" ];then echo "$b";else echo "b is null";fi
b is null
  • 命令可以作为判断结果

if grep -q '123' 1.txt; then  表示如果1.txt中含有'123'的行时会怎么样

[root@arslinux-01 shell]# if grep -w 'user1' /etc/passwd;then echo "user1 exist";fi
user1:x:1001:1001::/home/user1:/bin/bash
user1 exist
[root@arslinux-01 shell]# if grep -wq 'user1' /etc/passwd;then echo "user1 exist";fi
user1 exist

grep -q 静默输出


if [ ! -e file ]; then 表示文件不存在时会怎么样

if (($a<1)); then …等同于 if [ $a -lt 1 ]; then…

[ ] 中不能使用<,>,==,!=,>=,<=这样的符号


20.8/20.9 case 判断

格式:case  变量名 in

                value1)

                    command

                    ;;

                value2) 

                    command

                    ;;

                *)

                    commond

                    ;;

                esac

:: 表示一个判断结束,进入下一个判断

在case程序中,可以在条件中使用|,表示或的意思, 比如

2|3)

     command

    ;;

*)除以上所有之外的


shell 脚本案例

#!/bin/bash
read -p "Please input a number: " n           //从标准输入读取输入并赋值给变量 n
if [ -z "$n" ]
then
    echo "Please input a number."
exit 1
fi
n1=`echo $n|sed 's/[0-9]//g'`        //从变量n中将数字替换为空,如果n1不为空,则不是纯数字,那么现实输入一个数字并退出
if [ -n "$n1" ]
then
    echo "Please input a number."
exit 1
fi
if [ $n -lt 60 ] && [ $n -ge 0 ]//纯数字往下接着判断
then
    tag=1                                //标记tag
elif [ $n -ge 60 ] && [ $n -lt 80 ]
then
    tag=2
elif [ $n -ge 80 ]  && [ $n -lt 90 ]
then
    tag=3
elif [ $n -ge 90 ] && [ $n -le 100 ]
then
    tag=4
else
    tag=0
fi
case $tag in
    1)
        echo "not ok"
        ;;
    2)
        echo "ok"
        ;;
    3)
        echo "great"
        ;;
    4)
        echo "brilliant"
        ;;
    *)
        echo "The number range is 0-100."
        ;;
esac

执行脚本看结果:

输入符合条件的纯数字

[root@arslinux-01 shell]# sh -x case.sh
+ read -p 'Please input a number: ' n
Please input a number: 77
+ '[' -z 77 ']'
++ echo 77
++ sed 's/[0-9]//g'
+ n1=
+ '[' -n '' ']'
+ '[' 77 -lt 60 ']'
+ '[' 77 -ge 60 ']'
+ '[' 77 -lt 80 ']'
+ tag=2
+ case $tag in
+ echo ok
ok

输入含字母的数字组合

[root@arslinux-01 shell]# sh -x case.sh
+ read -p 'Please input a number: ' n
Please input a number: l33l
+ '[' -z l33l ']'
++ echo l33l
++ sed 's/[0-9]//g'
+ n1=ll
+ '[' -n ll ']'
+ echo 'Please input a number.'
Please input a number.
+ exit 1

输入大于 100 的数字

[root@arslinux-01 shell]# sh -x case.sh
+ read -p 'Please input a number: ' n
Please input a number: 234
+ '[' -z 234 ']'
++ echo 234
++ sed 's/[0-9]//g'
+ n1=
+ '[' -n '' ']'
+ '[' 234 -lt 60 ']'
+ '[' 234 -ge 60 ']'
+ '[' 234 -lt 80 ']'
+ '[' 234 -ge 80 ']'
+ '[' 234 -lt 90 ']'
+ '[' 234 -ge 90 ']'
+ '[' 234 -le 100 ']'
+ tag=0
+ case $tag in
+ echo 'The number range is 0-100.'
The number range is 0-100.


20.10 for 循环

  • 语法:for 变量名 in 条件; do …; done

案例1

计算1到100数字的和

[root@arslinux-01 shell]# vim for1.sh
#!/bin/bash
sum=0
for i in `seq 1 100`
do
    sum=$[$sum+$i]
done
echo $sum

案例2

列出/etc/下的目录或子目录

[root@arslinux-01 shell]# sh for2.sh
#!/bin/bash/
cd /etc/
for a in `ls /etc/`
do
    if [ -d $a ]
    then
        ls -d $a
    fi
done

ls 是用空格或回车作为分隔符,for 循环以他为对象,那么可能会出错


20.11/20.12 while循环

  • 语法:while 条件; do … ; done

案例1

每隔半分钟检查系统负载,当负载大于10时,发邮件

[root@arslinux-01 shell]# vim while.sh
#!/bin/bash
while :
do
    load=`w|head -1|awk -F 'load average: ' '{print $2}'|cut -d. -f1`
    if [ $load -gt 10]
    then
        /usr/local/sbin/mail.py [email protected] "load high" "$load"
    fi
    sleep 30
done

: 表示死循环 或者写成 while true


案例2

做一个让人让用户不停输入东西的脚本

[root@arslinux-01 shell]# vim while2.sh
#!/bin/bash
while :
do
    read -p "Please input a number: " n
    if [ -z "$n" ]
    then
        echo "you need input sth."
        continue
    fi
    n1=`echo $n|sed 's/[0-9]//g'`
    if [ -n "$n1" ]
    then
        echo "you just only input numbers."
        continue
    fi
    break
done
echo $n

continue 指继续重新再来一遍循环

continue 从头继续重新再来一遍循环

break 退出 while 循环


20.13 break跳出循环

[root@arslinux-01 shell]# vim break.sh
#!/bin/bash
for i in `seq 1 5`
do
    echo $i
    if [ $i -eq 3 ]
    then
        break
    fi
    echo $i
done
echo jjjjj
[root@arslinux-01 shell]# sh -x break.sh
++ seq 1 5
+ for i in '`seq 1 5`'
+ echo 1
1
+ '[' 1 -eq 3 ']'
+ echo 1
1
+ for i in '`seq 1 5`'
+ echo 2
2
+ '[' 2 -eq 3 ']'
+ echo 2
2
+ for i in '`seq 1 5`'
+ echo 3
3
+ '[' 3 -eq 3 ']'
+ break
+ echo jjjjj
jjjjj

原本在 i 为 3 之前,脚本一直执行到done,然后在从头循环,而 为 3 时,之间break跳出循环,

echo  aaaaaaa

break 用在循环语句里,for 也行,while 也行


20.14 continue结束本次循环

  • 忽略continue之下的代码,直接进行下一次循环

[root@arslinux-01 shell]# vim continue.sh
#!/bin/bash
for i in `seq 1 5`
do
    echo $i
    if [ $i -eq 3 ]
    then
        continue
    fi
    echo $i
done
echo jjjjj
[root@arslinux-01 shell]# sh -x continue.sh
++ seq 1 5
+ for i in '`seq 1 5`'
+ echo 1
1
+ '[' 1 -eq 3 ']'
+ echo 1
1
+ for i in '`seq 1 5`'
+ echo 2
2
+ '[' 2 -eq 3 ']'
+ echo 2
2
+ for i in '`seq 1 5`'
+ echo 3
3
+ '[' 3 -eq 3 ']'
+ continue
+ for i in '`seq 1 5`'
+ echo 4
4
+ '[' 4 -eq 3 ']'
+ echo 4
4
+ for i in '`seq 1 5`'
+ echo 5
5
+ '[' 5 -eq 3 ']'
+ echo 5
5
+ echo jjjjj
jjjjj

break会跳出循环,不再执行循环,而continue会结束本次循环,从头开始执行循环


20.15 exit退出整个脚本

[root@arslinux-01 shell]# vim exit.sh
#!/bin/bash
for i in `seq 1 5`
do
    echo $i
    if [ $i -eq 3 ]
    then
        exit
    fi
    echo $i
done
echo jjjjj
[root@arslinux-01 shell]# sh -x exit.sh
++ seq 1 5
+ for i in '`seq 1 5`'
+ echo 1
1
+ '[' 1 -eq 3 ']'
+ echo 1
1
+ for i in '`seq 1 5`'
+ echo 2
2
+ '[' 2 -eq 3 ']'
+ echo 2
2
+ for i in '`seq 1 5`'
+ echo 3
3
+ '[' 3 -eq 3 ']'
+ exit

break 在跳出后还会执行最后的 echo,而 exit 直接退出


  • 给 exit 定义退出的数字

[root@arslinux-01 shell]# vim exit.sh
#!/bin/bash
for i in `seq 1 5`
do
    echo $i
    if [ $i -eq 3 ]
    then
        exit 2
    fi
    echo $i
done
echo jjjjj
[root@arslinux-01 shell]# sh -x exit2.sh
++ seq 1 5
+ for i in '`seq 1 5`'
+ echo 1
1
+ '[' 1 -eq 3 ']'
+ echo 1
1
+ for i in '`seq 1 5`'
+ echo 2
2
+ '[' 2 -eq 3 ']'
+ echo 2
2
+ for i in '`seq 1 5`'
+ echo 3
3
+ '[' 3 -eq 3 ']'
+ exit 2
[root@arslinux-01 shell]# echo $?
2

continue 结束本次循环,继续循环,忽略后面的代码

break 结束整个循环,跳出

exit 直接退出整个脚本


扩展

select用法 http://www.apelearn.com/bbs/thread-7950-1-1.html

入职后的流程:http://ask.apelearn.com/question/18013