20.1 Shell脚本介绍
- shell是一种脚本语言
- 可以使用逻辑判断、循环等语法
- 可以自定义函数
- shell是系统命令的集合
- shell脚本可以实现自动化运维,能大大增加我们的运维效率
- 解决日常运维工作中的重复工作
20.2 Shell脚本结构和执行
shell脚本结构和执行方法
- 开头需要加#!/bin/bash,在本机上省略了能够执行,但是换一个机器就不一定能行了
- 以#开头的行作为解释说明
- 脚本的名字以.sh结尾,用于区分这是一个shell脚本
- 执行方法有两种
- chmod a+x 1.sh; ./1.sh
- bash 1.sh
- 查看脚本执行过程 bash -x 1.sh
- 查看脚本是否语法错误 bash -n 1.sh
1、简单shell脚本
#!/bin/bash echo 123 w ls
在本机上省略#!/bin/bash
echo 123 w ls
执行sh 1.sh,是能够执行的,但是换一个机器就不一定能行了。
[root@liang-00 shell]# sh 1.sh 123 17:06:38 up 6:02, 1 user, load average: 0.00, 0.01, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.37.1 16:42 6.00s 2.00s 0.01s w 1.sh [root@liang-00 shell]#
#!/bin/bash意思是说下面的脚本时能够让/bin/bash解析的。
加上 #!/bin/bash 给1.sh赋予x权限:chmod a+x 1.sh 后才能够直接执行。
[root@liang-00 shell]# chmod a+x 1.sh [root@liang-00 shell]# ./1.sh 123 17:11:50 up 6:07, 1 user, load average: 0.00, 0.01, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.37.1 16:42 6.00s 2.07s 0.00s /bin/bash ./1.sh 1.sh [root@liang-00 shell]#
2、其实 /bin/bash和/bin/sh 是同一个文件
[root@liang-00 shell]# ll /bin/bash -rwxr-xr-x. 1 root root 964544 Apr 11 2018 /bin/bash [root@liang-00 shell]# which sh /usr/bin/sh [root@liang-00 shell]# ll /bin/sh lrwxrwxrwx. 1 root root 4 Oct 16 18:00 /bin/sh -> bash [root@liang-00 shell]#
#!/bin/bash放在第一行才表示用/bin/bash解析,以后的#开头的表示解释说明。
3、sh -x 1.sh 表示查看脚本执行过程,每个“+”表示一个步骤。
[root@liang-00 shell]# sh -x 1.sh + echo 123 123 + w 17:20:19 up 6:16, 1 user, load average: 0.00, 0.01, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.37.1 16:42 3.00s 2.48s 0.01s w + ls 1.sh [root@liang-00 shell]#
4、sh -n 1.sh 查看脚本语法是否有错误。
20.3 date命令用法
date命令在shell脚本中作用很大。标记和更改文件,加上日期标记。
1、日期时间标记。
+%Y:四位的年2018
+%y:两位的年18
+%m:月份
+%M:分钟
+%d:日期
+%D:标记一个年月日(01/08/19)
+%F:带“-”的年月日
+%H:小时
+%S:秒
+%s:表示时间戳,距离1970年1月1日多长时间,单位秒。
+%T:等于+%H:%M:%S
+%w:星期几
+%W:今年的第几周
cal:以日历的格式查看日期
cal命令,日历格式显示
[root@liang-00 shell]# cal January 2019 Su Mo Tu We Th Fr Sa 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 [root@liang-00 shell]#
date -d “-1 day” 一天前,具体的还有month、year、hour、min、sec等。
[root@liang-00 shell]# date -d "-1 day" Mon Jan 7 17:48:13 CST 2019 [root@liang-00 shell]# date -d "-1 month" Sat Dec 8 17:48:23 CST 2018 [root@liang-00 shell]# date -d "-1 year" Mon Jan 8 17:48:28 CST 2018 [root@liang-00 shell]# date -d "-1 hour" Tue Jan 8 16:48:47 CST 2019 [root@liang-00 shell]# date -d "-1 min" Tue Jan 8 17:48:03 CST 2019 [root@liang-00 shell]# date -d "-1 sec" Tue Jan 8 17:49:29 CST 2019 [root@liang-00 shell]#
date -d +%s 时间戳
date -d @1546941077 时间戳和具体日期换算(把时间戳转换成具体时间)。
date +%s -d “2019-1-8 17:51:17” 把具体的时间转换成相应的时间戳。
[root@liang-00 shell]# date +%s 1546941077 [root@liang-00 shell]# date -d @1546941077 Tue Jan 8 17:51:17 CST 2019 [root@liang-00 shell]# date +%s -d "2019-1-8 17:51:17" 1546941077 [root@liang-00 shell]#
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
- 格式2:if 条件; then 语句; else 语句; fi
- 格式3:if …; then … ;elif …; then …; else …; fi
- 逻辑判断表达式:if [ $a -gt $b ]; if [ $a -lt 5 ]; if [ $b -eq 10 ]等 -gt (>); -lt(<); -ge(>=); -le(<=);-eq(==); -ne(!=) 注意到处都是空格
- 可以使用 && || 结合多个条件
- if [ $a -gt 5 ] && [ $a -lt 10 ]; then
- if [ $b -gt 5 ] || [ $b -lt 3 ]; then
1、if的第一种格式if then。
命令行写出。
[root@liang-00 shell]# a=4 [root@liang-00 shell]# if [ $a -gt 3 ] > then > echo OK > fi OK [root@liang-00 shell]# if [ $a -gt 3 ]; then echo OK; fi OK [root@liang-00 shell]#
在 if 这种格式或 shell 中每个参数之间都要有“空格”。
在shell脚本中表示。
[root@liang-00 shell]# vi if_1.sh
[root@liang-00 shell]# cat if_1.sh #!/bin/bash a=4 if [ $a -gt 3 ] then echo OK fi [root@liang-00 shell]# sh if_1.sh OK
2、else(注意空格)
[root@liang-00 shell]# cat if_1.sh #!/bin/bash a=1 if [ $a -gt 3 ] then echo OK else echo not OK fi [root@liang-00 shell]# sh -x if_1.sh + a=1 + '[' 1 -gt 3 ']' + echo not OK not OK [root@liang-00 shell]#
3、elif(注意空格)
[root@liang-00 shell]# cat if_1.sh #!/bin/bash a=1 if [ $a -gt 3 ] then echo "a>3" elif [ $a -gt 0 ] then echo "3>a>0" else echo "a<0" fi [root@liang-00 shell]# sh -x if_1.sh + a=1 + '[' 1 -gt 3 ']' + '[' 1 -gt 0 ']' + echo '3>a>0' 3>a>0 [root@liang-00 shell]#
4、&& 表示并且,|| 表示或者
[root@liang-00 shell]# sh -x if_2.sh + a=6 + '[' 6 -gt 0 ']' + '[' 6 -lt 5 ']' + echo 'a>=5' a>=5 [root@liang-00 shell]# cat if_2.sh #!/bin/bash a=6 if [ $a -gt 0 ] && [ $a -lt 5 ] then echo "5>a>0" else echo "a>=5" fi [root@liang-00 shell]#
20.6 文件目录属性判断
- [ -f file ]判断是否是普通文件,且存在
- [ -d file ] 判断是否是目录,且存在
- [ -e file ] 判断文件或目录是否存在
- [ -r file ] 判断文件是否可读
- [ -w file ] 判断文件是否可写
- [ -x file ] 判断文件是否可执行
1、shell脚本判断一个文件是否存在
[root@liang-00 shell]# sh -x file_1.sh + f=/tmp/testfile + '[' -f /tmp/testfile ']' + touch /tmp/testfile [root@liang-00 shell]# cat file_1.sh #!/bin/bash f="/tmp/testfile" if [ -f $f ] then echo $f else touch $f fi [root@liang-00 shell]#
是否为命目录。
[root@liang-00 shell]# vi file_1.sh [root@liang-00 shell]# sh -x file_1.sh + f=/tmp/testfile + '[' -d /tmp/testfile ']' + touch /tmp/testfile
是否存在。
[root@liang-00 shell]# vi file_1.sh [root@liang-00 shell]# sh -x file_1.sh + f=/tmp/testfile + '[' -e /tmp/testfile ']' + echo /tmp/testfile /tmp/testfile [root@liang-00 shell]#
用&&来执行命令:
f="/tmp/testfile"
[ -f $f ] && rm -f $f 等同于
if [ -f $f ]
then
rm -f $f
fi
判断文件不存在用"!"。
[ ! -f $f ] && rm -f $f
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…
- [ ] 中不能使用<,>,==,!=,>=,<=这样的符号
1、判断变量为空时
#!/bin/bash n=`wc -l /tmp/aaa` if [ -z "$n" ] then echo "n not exist." exit elif [ $n -gt 100 ] then echo "ssssssss" fi
改良,先判断文件是否存在
if [ ! -f /tmp/aaa ] then echo "/tmp/aaa not exist." exit fi n=`wc -l /tmp/aaa` if [ $n -gt 100 ] then echo "ssssssss" fi
2、判断不为空 if [ -n "$a" ]
[root@liang-00 shell]# echo $b [root@liang-00 shell]# if [ -n "$b" ];then echo $b;else echo "b is null";fi b is null [root@liang-00 shell]#
3、if grep 用法
[root@liang-00 shell]# if grep -wq 'liang' /etc/passwd ;then echo "liang is exist";fi liang is exist [root@liang-00 shell]# if ! grep -wq 'liang' /etc/passwd ;then useradd liang;fi [root@liang-00 shell]#
-w表示匹配只单词,-q表示只执行不输出内容
20.8 case判断
格式 case 变量名 in
value1)
command
;;
value2)
command
;;
*)
commond
;;
esac
在case程序中,可以在条件中使用“|”,表示或的意思, 比如:
2|3)
command
;;
1、shell脚本案例,考试成绩判断:
#!/bin/bash read -p "Please input a number: " n #用户交互输入成绩 if [ -z "$n" ] #判断是否为空 then echo "Please input a number." exit 1 fi n1=`echo $n|sed 's/[0-9]//g'` #判断是否为纯数字 if [ -n "$n1" ] then echo "Please input a number." exit 1 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 case $tag in 1) echo "not ok" ;; 2) echo "ok" ;; 3) echo "ook" ;; 4) echo "oook" ;; *) echo "The number range is 0-100." ;; esac
read -p "Please input your mark: " n 获得一个用户输入的值,赋予n
[root@liang-00 shell]# read -p "Please input your mark: " n Please input your mark: 90 [root@liang-00 shell]# echo $n 90 [root@liang-00 shell]#
exit 1 表示执行完命令后退出的状态。
[root@liang-00 shell]# sh test.sh Please input a number: Please input a number. [root@liang-00 shell]# echo $? 1 [root@liang-00 shell]# cat test.sh #!/bin/bash read -p "Please input a number: " n if [ -z "$n" ] then echo "Please input a number." exit 1 fi [root@liang-00 shell]#
执行shell脚本
[root@liang-00 shell]# sh -x case.sh + read -p 'Please input a number: ' n Please input a number: 90 + '[' -z 90 ']' ++ sed 's/[0-9]//g' ++ echo 90 + n1= + '[' -n '' ']' + '[' 90 -lt 60 ']' + '[' 90 -ge 60 ']' + '[' 90 -lt 80 ']' + '[' 90 -ge 80 ']' + '[' 90 -lt 90 ']' + '[' 90 -ge 90 ']' + '[' 90 -le 100 ']' + tag=4 + case $tag in + echo oook oook [root@liang-00 shell]# sh -x case.sh + read -p 'Please input a number: ' n Please input a number: 80 + '[' -z 80 ']' ++ echo 80 ++ sed 's/[0-9]//g' + n1= + '[' -n '' ']' + '[' 80 -lt 60 ']' + '[' 80 -ge 60 ']' + '[' 80 -lt 80 ']' + '[' 80 -ge 80 ']' + '[' 80 -lt 90 ']' + tag=3 + case $tag in + echo ook ook [root@liang-00 shell]# sh -x case.sh + read -p 'Please input a number: ' n Please input a number: 101 + '[' -z 101 ']' ++ echo 101 ++ sed 's/[0-9]//g' + n1= + '[' -n '' ']' + '[' 101 -lt 60 ']' + '[' 101 -ge 60 ']' + '[' 101 -lt 80 ']' + '[' 101 -ge 80 ']' + '[' 101 -lt 90 ']' + '[' 101 -ge 90 ']' + '[' 101 -le 100 ']' + tag=0 + case $tag in + echo 'The number range is 0-100.' The number range is 0-100. [root@liang-00 shell]# sh -x case.sh + read -p 'Please input a number: ' n Please input a number: sa + '[' -z sa ']' ++ sed 's/[0-9]//g' ++ echo sa + n1=sa + '[' -n sa ']' + echo 'Please input a number.' Please input a number. + exit 1 [root@liang-00 shell]#
20.10 for循环
- 语法:for 变量名 in 条件; do …; done
- 案例1:求和1-100
#!/bin/bash
sum=0
for i in `seq 1 100`
do
sum=$[$sum+$i]
echo $i
done
echo $sum
1、数字求和。
首先实现打印出1-100
#!/bin/bash for i in `seq 1 100` do echo $i done ~
再求1-100的和,最后打印出总和。
#!/bin/bash sum=0 for i in `seq 1 100` do sum=$[$sum+$i] done echo $sum
seq命令:用于产生从某个数到另外一个数之间的所有整数。
[root@liang-00 shell]# seq 1 10 1 2 3 4 5 6 7 8 9 10 [root@liang-00 shell]# seq 1 10 |xargs 1 2 3 4 5 6 7 8 9 10 [root@liang-00 shell]#
2、对文件目录for循环。
进入/etc目录,遍历etc下的内容,如果是目录的话,只列出该目录。
#!/bin/bash cd /etc/ 先进入到目录下在for循环 for a in `ls /etc/` do if [ -d $a ] then ls -d $a fi done
执行脚本
[root@liang-00 shell]# sh for2.sh alternatives audisp audit bash_completion.d binfmt.d chkconfig.d cron.d cron.daily cron.hourly cron.monthly cron.weekly dbus-1 default depmod.d dhcp dnsmasq.d dracut.conf.d exports.d firewalld fonts ...............
3、for在遍历的时候是以回车符或空格来分割元素的,所以文件名字中有空格的情况下,for会把文件识别错
例如:“3 4.txt”是一个文件,在做for循环遍历是会把它当做两个文件来处理。
[root@liang-00 liang]# touch 1 2 3\ 4.txt [root@liang-00 liang]# ll total 0 -rw-r--r-- 1 root root 0 Jan 9 00:04 1 -rw-r--r-- 1 root root 0 Jan 9 00:04 2 -rw-r--r-- 1 root root 0 Jan 9 00:04 3 4.txt [root@liang-00 liang]# for i in `ls` ;do echo $i ;done 1 2 3 4.txt [root@liang-00 liang]#
20.11 while循环
- 语法 while 条件; do … ; done
案例1:每隔半分钟检查下负载,当系统负载大于10时发送邮件。
#!/bin/bash
while :
do
load=`uptime|awk -F 'load average: ' '{print $2}'|cut -d. -f1`
if [ $load -gt 10 ]
then
top|mail -s "load is high: $load" [email protected]
fi
sleep 30
done
其中:while后的冒号表示死循环,还可以是true,也表示死循环。
[root@liang-00 liang]# sh -x while1.sh + true ++ uptime ++ awk -F 'load average: ' '{print $2}' ++ cut -d. -f1 + load=0 + '[' 0 -gt 10 ']' + sleep 30
为了不让脚本意外终止,可以打开一个screen,在screen里执行。
案例2:得到想要的结果在退出循环。
#!/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
执行脚本:
[root@liang-00 shell]# sh while2.sh Please input a number: www1111111 you just only input numbers. Please input a number: 2222222222222111111111111111 2222222222222111111111111111 [root@liang-00 shell]#
20.13 break跳出循环
在for循环中用break
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i -eq 3 ] then break fi echo $i done echo aaaaaaa
执行脚本:
[root@liang-00 shell]# sh for_break.sh 1 1 2 2 3 aaaaaaaaaaaa [root@liang-00 shell]#
20.14 continue结束本次循环
- 忽略continue之下的代码,直接进行下一次循环
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i == 3 ] then continue fi echo $i done echo $i
执行脚本:
[root@liang-00 shell]# sh for_break.sh 1 1 2 2 3 4 4 5 5 5 [root@liang-00 shell]#
20.15 exit退出整个脚本
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i == 3 ] then exit fi echo $i done echo aaaaaaa
执行脚本:
[root@liang-00 shell]# sh for_break.sh 1 1 2 2 3 [root@liang-00 shell]#
扩展:
select用法:http://www.apelearn.com/bbs/thread-7950-1-1.html