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
可以看到第三次,直接退出了脚本