20.1 shell脚本介绍

•shell是一种脚本语言
•可以使用逻辑判断、循环等语法
•可以自定义函数
•shell是系统命令的集合
•shell脚本可以实现自动化运维,能大大增加我们的运维效率

shell是什么 shell是一个命令解释器,它在操作系统的最外层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户。这种对话方式可以是交互的方式(从键盘输入命令,可以立即得到shell的回应),或非交互(执行脚本程序)的方式 什么是shell脚本? 当linux命令或语句不在命令下执行(严格说,命令行执行的语句也是shell脚本),而是通过一个程序文件执行时,该程序就被称为shell脚本或shell程序,shell程序很类似DOS系统下的批处理程序(扩展名*.bat)。用户可以在shell脚本中敲入一系列的命令或命令语句组合。这些命令、变量和流程控制语句等有机的结合起来就形成了一个功能很强大的shell脚本。 **shell脚本语言的种类 ** 在UNIX/LINUX中主要有两大类shell。

•Bourne shell包括sh、ksh和bash。
•Bourne shell (sh)
•Korn shell (ksh)
•Bourne Again shell (bash)
•POSIX shll (sh)
•C shell包括csh和tcsh。
•C shell (csh)
•TEXES/TOPS C shell(tcsh)

shell脚本语言是若类型语言,较为通用的shell有标准的Bourne shell(sh)和C shell(csh)。其中Bource shell(sh)已经被bash shell取代。 查看系统的shell。

[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash #功能更强大
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh

20.2 shell脚本结构和执行

•开头需要加#!/bin/bash,指定是哪个解释器 •以#开头的作为解释说明 •脚本的名字以.sh结尾,用于区分这是一个shell脚本 •执行方法有几种 shell脚本的建立 在linux系统中,shell脚本(bash shll程序)通常是在编辑器(如vi/vim)中编写,由Unix/Linux命令、bash shell命令、程序结构控制语句和注释等内容组成 脚本开头(第一行) 一个规范的shell脚本在脚本第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在linux bash编程中一般为: #! /bin/bash 或 #! /bin/sh <==255个字符以内 其中开头的"#!"字符又称为幻数,在执行bash脚本的时候,内核会根据"#!"后的解释器来确定该用哪个程序解释这个脚本的内容。注意,这一行必须在每个脚本顶端的第一行,如果不是第一行则为脚本注释行 脚本注释 在shell脚本中,跟在(#)井号后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当做程序执行,仅仅是给用户看,系统解释器是看不到的,更不会执行。注释可自成一行,也可以跟在脚本命令后面与命令在同一行 shell脚本的执行 当shell脚本运行时,它会先查找系统环境变量ENV,该变量指定了环境文件(通常是.bashrc、.bash_profile、/etc/bashrc、/etc/profile等),然后从该环境变量文件开始执行脚本,当读取了ENV的文件后,shell会开始执行shell脚本中的内容。 特殊技巧:设置crond任务时,最好把系统环境变量在定时任务脚本中重新定义,否则,一些系统环境变量将不会被加载 执行说明 第一种方法是当脚本文件本身没有可执行权限(即文件x位为-号)时常使用的方法,或者文件开头没有指定解释器。 例如:

[root@localhost ~]# cat test.sh 
echo 'i am shell'
[root@localhost ~]# sh test.sh 
i am shell
[root@localhost ~]# bash test.sh 
i am shell

第二种方法需要先将脚本文件的权限改为可执行(即文件加X位),具体方法:chmod u+x script-name或chmod 755 script-name。然后通过脚本路径就可以直接执行脚本。 例如:

[root@localhost ~]# ll test.sh 
-rw-rw-r--. 1 root root 1217 7月  27 13:57  test.sh
[root@localhost ~]# ./test.sh #使用第二种方式,"./"在当前目录下执行test.sh脚本文件,无法使用tab自动补全
-bash: ./test.sh: Permission denied 
给test.sh添加可执行权限。
[root@localhost ~]# chmod u+x test.sh 
[root@localhost ~]# ./test.sh 
i am shell

第三种放法通常是使用source或"."点号读入或加载指定的shell脚本文件(如test.sh),然后,依次执行指定shell脚本文件test.sh中的所有语句 例如:

[root@lamp ~]# . test.sh 
i am shell
[root@lamp ~]# source test.sh 
i am shell

说明:如果希望父shell调用子shell的变量、函数等,用source或"."点号执行脚本

20.3 date命令用法

命令:date date 可以用来显示或设定系统的日期与时间。 命令参数

-d<字符串>:显示字符串所指的日期与时间。字符串前后必须加上双引号; 
-s<字符串>:根据字符串来设置日期与时间。字符串前后必须加上双引号;只有root权限才能设置,其他只能查看 
-u:显示GMT; 
--help:在线帮助; 
--version:显示版本信息

日期格式字符串列表

%H 小时(以00-23来表示)。 
%I 小时(以01-12来表示)。 
%K 小时(以0-23来表示)。 
%l 小时(以0-12来表示)。 
%M 分钟(以00-59来表示)。 
%P AM或PM。 
%r 时间(含时分秒,小时以12小时AM/PM来表示)。 
%s 总秒数。起算时间为1970-01-01 00:00:00 UTC。 
%S 秒(以本地的惯用法来表示)。 
%T 时间(含时分秒,小时以24小时制来表示)。 
%X 时间(以本地的惯用法来表示)。 
%Z 市区。 
%a 星期的缩写。 
%A 星期的完整名称。 
%b 月份英文名的缩写。 
%B 月份的完整英文名称。 
%c 日期与时间。只输入date指令也会显示同样的结果。 
%d 日期(以01-31来表示)。 
%D 日期(含年月日)。 
%j 该年中的第几天。 
%m 月份(以01-12来表示)。 
%U 该年中的周数。 
%w 该周的天数,0代表周日,1代表周一,异词类推。 
%x 日期(以本地的惯用法来表示)。 
%y 年份(以00-99来表示)。 
%Y 年份(以四位数来表示)。 
%n 在显示时,插入新的一行。 
%t 在显示时,插入tab。 
MM 月份(必要) 
DD 日期(必要) 
hh 小时(必要) 
mm 分钟(必要)
ss 秒(选择性)

实例: •年月日

[root@localhost ~]# date +"%Y-%m-%d" 
2018-07-29
[root@localhost ~]# date +"%y-%m-%d" 
18-07-29

•输出昨天、前天,明天等的日期:

[root@localhost ~]# date -d "+1 day " +"%Y-%m-%d"
2018-07-28
[root@localhost ~]# date -d "+2 day " +"%Y-%m-%d"
2018-07-27
[root@localhost ~]# date -d "-1 day " +"%Y-%m-%d"
2018-07-30

•加减操作:

date -d "+1 day" +%Y%m%d   #显示前一天的日期 
date -d "-1 day" +%Y%m%d   #显示后一天的日期 
date -d "-1 month" +%Y%m%d #显示上一月的日期 
date -d "+1 month" +%Y%m%d #显示下一月的日期 
date -d "-1 year" +%Y%m%d  #显示前一年的日期 
date -d "+1 year" +%Y%m%d  #显示下一年的日期

设定时间:

date -s          #设置当前时间,只有root权限才能设置,其他只能查看 
date -s 20120523 #设置成20120523,这样会把具体时间设置成空00:00:00 
date -s 01:01:01 #设置具体时间,不会对日期做更改 
date -s "01:01:01 2012-05-23" #这样可以设置全部时间 
date -s "01:01:01 20120523"   #这样可以设置全部时间 
date -s "2012-05-23 01:01:01" #这样可以设置全部时间 
date -s "20120523 01:01:01"   #这样可以设置全部时间

•显示日历

[root@localhost ~]# cal
      七月 2018     
日 一 二 三 四 五 六
 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

20.4 shell脚本中的变量

•当脚本中使用某个字符串,多次频繁并且字符串长度很长时,就应该使用变量代替 •使用条件语句时,常使用变量 if [ $a -gt 1];then ...;fi •引用某个命令的结果时,用变量替代 n=wc -l 1.txt •写和用户交互的脚本时,变量也是必不可少的 read -p "lnput 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脚本中的逻辑判断

Shell脚本中,充满着各种逻辑判断,是脚本中必备的 •if逻辑判断格式

格式1:if 条件 ; then 语句; fi
格式2:if 条件; then 语句; else 语句; fi
格式3:if …; then … ;elif …; then …; else …; fi

•数学逻辑判断表达式:

-gt (>);    大于    great than
-lt(<);      小于     less than
-ge(>=);  大于或等于   
-le(<=);   小于或等于
-eq(==);  等于     equal
-ne(!=)    不等于  not equa
- - - - - - - - - - - - - - - - - - - - - -
方括号里面不能使用数学运算符<,>,=,等,两边中间都要有空格
要使用这些符号,只能使用两个括号(()),不用空格
例如
if [ $a -gt $b ]等同if (($a>$b))
if [ $a -lt 5 ]等同if (($a<5))
if [ $b -eq 10 ]等if (($b==10))

•多个条件组合-a,-o

-a表示并且,全部条件都成立
-o或者或者,满足一个条件成立
[root@localhost ~]# a=5;if [ $a -gt 4 -a $a -lt 6 ];then echo ok;fi
ok
[root@localhost ~]# a=5;if [ $a -gt 5 -o $a -lt 6 ];then echo ok;fi
ok

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

条件A&&条件B:A并且B
条件A||条件B:A或者B
if [ $a -gt 5 ] && [ $a -lt 10 ]; then
if [ $b -gt 5 ] || [ $b -lt 3 ]; then

•if逻辑判断例子 格式1:if 条件 ; then 语句; fi

#!/bin/bash
a=5
if [ $a -gt 3 ]
then
       echo "ok"
fi

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

#!/bin/bash
a=5
if [ $a -gt 3 ]
then
    echo "ok"
else
    echo "nook"
fi

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

#!/bin/bash
a=5
if [ $a -lt 4 ]
then
    echo "<4"
elif [ $a -lt 6 ]
#注意elif可以嵌套多次的
then
    echo "<6 && >4"
else
    echo "nook"
fi

20.6 文件目录属性判断

在shell中通常要和文件或者目录打交道,那么对于他们的属性判断十分重要

[ -f file ]判断是否是普通文件,且存在   
[ -d file ] 判断是否是目录,且存在  
[ -e file ] 判断文件或目录是否存在  
[ -r file ] 判断文件是否可读   
[ -w file ] 判断文件是否可写  
[ -x file ] 判断文件是否可执行

例子:

#!/bin/bash
file="/tmp/1.txt"
if [ -e $file ]
then 
       echo $file exist
else
      touch $file

&& ||特殊用法,省略if

1.txt不存在,&&表示前一条命令执行成功,才会执行后面的命令
[root@localhost ~]# [ -e 1.txt ]&&touch 1.txt
[root@localhost ~]# ll
总用量 4
-rw-------. 1 root root 1418 4月  26 05:41 anaconda-ks.cfg
drwxr-xr-x  2 root root   60 6月  18 11:40 src1
1.txt不存在,||表示前一条命令执行不成功,才会执行后面的命令
[root@localhost ~]# [ -e 1.txt ]||touch 1.txt
[root@localhost ~]# ll
总用量 4
-rw-r--r--  1 root root    0 7月  29 22:13 1.txt
-rw-------. 1 root root 1418 4月  26 05:41 anaconda-ks.cfg
drwxr-xr-x  2 root root   60 6月  18 11:40 src1

20.7 if特殊用法

if [ -z "$a" ]  这个表示当变量a的值为空时会怎么样
if [ -n "$a" ]  表示当变量a的值不为空
if grep -q '123' 1.txt; then  表示如果1.txt中含有'123'的行时会怎么样
if [ ! -e file ]; then 表示文件不存在时会怎么样
if (($a<1)); then …等同于 if [ $a -lt 1 ]; then… 
[ ] 中不能使用<,>,==,!=,>=,<=这样的符号

例子: if [ -z “$a” ] 这个表示当变量a的值为空时会怎么样

#!/bin/bash
n='wc -l /tmp/1.txt'
if [ $n -lt 100 ]
then
       echo "line num less than 100"
fi
# 如果/tmp/1.txt文件为空,或者被删除的话,脚本就会运行出错,出现bug
应该加上一个判断条件
#!/bin/bash
n='wc -l /tmp/1.txt'
if [  -z "$n" ]
# [  -z "$n" ] 等同[ ! -n "$n" ],!表示取反
then
       echo "error"
       exit
elif [ $n -lt 100 ]
then
        echo "line num less than 100"
fi

或者
#!/bin/bash
if [ ! -f /tmp/1.txt ]
then
       echo "/tmp/1.txt is not exist"
       exit
fi

n='wc -l /tmp/1.txt'
if [ $n -lt 100 ]
then
        echo "line num less than 100"
fi

if [ -n “$a” ] 表示当变量a的值不为空,也可以判断文件,判断文件时可以不加双引号

if [ -n 01.sh ]; then echo "ok"; fi
另外
#!/bin/bash
if [ -n "$b" ]
then  
       echo $b
else
        echo "b is null"
fi

一条命令也可以作为判断条件。判断user1用户是否存在

if grep -wq 'user1'  /etc/passwd; then echo "user1 is exist"; else useradd user1;fi 
#grep -w 精准匹配单词,加-q可以不显示过滤信息

20.8/20.9 case判断

case判断格式

case 变量名 in
    value1)
      commond1
      ;;
    value2)
      commod2
      ;;
    value3)
      commod3
      ;;
esac

在case中,可以在条件中使用“|”,表示或的意思,如:

2|3)
    commond
    ;;

例子: 输入一个同学的分数,判断成绩是否及格,优秀

[root@localhost sbin]# vim case1.sh
#!/bin/bash
read -p "Please input a number: " n
# read -p 是读取用户的输入数据,定义到变量里面
if [ -z "$n" ]
then
    echo "error not input"
    exit 1
#“exit 1”表示非正常运行导致退出程序
#退出之后,echo $?会返回1值,表示程序退出是因为出错了
fi

n1=`echo $n|sed 's/[0-9]//g'`
#判断用户输入的字符是否为纯数字
#如果是数字,则将其替换为空,赋值给$n1
if [ -n "$n1" ]
then
echo "Please input a number."
exit 1
#判断$n1不为空时(即$n不是纯数字)再次提示用户输入数字并退出
fi

#如果用户输入的是纯数字则执行以下命令:
if [ $n -lt 60 ] && [ $n -ge 0 ]
then
    tag=1
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
#tag的作用是为判断条件设定标签,方便后面引用
case $tag in
    1)
        echo "不及格"
        ;;
    2)
        echo "及格"
        ;;
    3)
        echo "良好"
        ;;
    4)
        echo "优秀"
        ;;
    *)
        echo "The number range is 0-100."
        ;;
esac

运行结果

[root@localhost shell]# sh case1.sh
Please input a number: 
error not input
[root@localhost shell]# sh case1.sh
Please input a number: 12a23
Please input a number.
[root@localhost shell]# sh case1.sh
Please input a number: 45
不及格
[root@localhost shell]# sh case1.sh
Please input a number: 65
及格
[root@localhost shell]# sh case1.sh
Please input a number: 85
良好
[root@localhost shell]# sh case1.sh
Please input a number: 95
优秀
[root@localhost shell]# sh case1.sh
Please input a number: 120
The number range is 0-100.

20.10 for循环

•格式:

for 变量名 in 条件; do ...; done

例子: •用for循环计算1到100的总和

[root@localhost shell]# cat for.sh
#!/bin/bash
sum=0
for i in `seq 1 100`
do
    sum=$[$sum+$i]
done
echo $sum

#输出的结果
[root@localhost shell]# sh for.sh 
5050

•文件列表循环

[root@localhost shell]# cat for2.sh 
#!/bin/bash
cd /etc/
for a in `ls /etc/`
do
    if [ -d $a ]
    then
        ls -d $a
    fi
done
#for循环是以空格、回车符作为分割符分割。

20.11/20.12 while循环

•语法 while 条件; do ...; done 例子: 用while循环检测系统1分钟的负载,如果大于,发邮件通知

#!/bin/bash
while :
#while 跟冒号表示死循环
do
    load=`w|head -1 |awk -F 'load average: ' '{print $2}'| cut -d . -f1`
    if [ $load -gt 10 ]
    then
        top|mail -s "load is high:$load" admin@163.com
    fi
    sleep 30
done

循环判断输入的是否纯数字

#!/bin/bash
while :
do
  read -p "Please input a number:" n
  if [ -z "$n" ]
  then
      echo "You did not enter the number."
      continue
  fi
  n1=`echo $n|sed 's/[0-9]//g'`
  if [ ! -z "$n1" ]
  then
      echo "You can only enter a pure number."
      continue
  fi
  break
done
echo $n

20.13 break跳出循环

break表示跳出循环,断续执行后面的语句 例子

#!/bin/bash
for i in `seq 1 5`
do
  echo $i
  if [ $i -eq 3 ]
  then
      break
  fi
  echo $i
done
echo aaaaaa

运行结果

[root@localhost ~]# sh break.sh
1
1
2
2
3
aaaaaa

可以看到上例中,只循环到第3次的时候,跳出了循环,直接执行了echo aaaaaa的语句

20.14 continue结束本次循环

continue结束本次循环,不执行后面的语句,直到循环结束

[root@localhost etc]# cat continue.sh 
#!/bin/bash
for i in `seq 1 5`
do
  echo $i
  if [ $i -eq 3 ]
  then
      continue
  fi
  echo $i
done
echo aaaaaa

运行结果

[root@localhost etc]# sh continue.sh 
1
1
2
2
3
4
4
5
5
aaaaaa

可以看到循环到第3次的时候,没有执行后面的语句,直到循环结束

20.15 exit退出整个脚本

exit退出脚本

[root@localhost etc]# cat exit.sh 
#!/bin/bash
for i in `seq 1 5`
do
  echo $i
  if [ $i -eq 3 ]
  then
      exit
  fi
  echo $i
done
echo aaaaaa

运行结果

[root@localhost etc]# sh exit.sh 
1
1
2
2
3

可以看到第三次,直接退出了脚本