写在前面
这则笔记主要整理shell脚本撰写的知识,主要包括:
shell脚本介绍
date命令
shell判断
shell循环
shell函数
shell数组
shell脚本介绍
shell脚本(shell script),是一种为shell编写的脚本程序。我们可以把需要执行的命令写在脚本当中,系统通过读取脚本执行命令。
shell脚本的特点如下;
shell是一种脚本语言;
可以使用逻辑判断、循环等语法;
可以自定义函数;
shell是系统命令的集合;
shell脚本可以实现自动化运维,能大大增加我们的运维效率;
关于shell脚本,有一些约定俗成的规矩:
脚本通常都以 .sh 作为后缀名;
注释虽然不是必须的,但为了便于后期维护和管理,强烈建议每个脚本都要写注释;
脚本建议统一放在/usr/local/sbin/ ,便于维护和管理;
shell脚本结构
一个shell脚本一般包括三个部分:
第一部分:#!/bin/sh,指定脚本解释器。
第二部分:# 开头的是注释。
第三部分:脚本代码。
shell脚本执行方式
方式一:
1
2
3
4
5//更改权限
chmod 755 a.sh
//执行脚本
a.sh
方法二:
1
2
3
4
5
6
7
8//执行脚本
bash a.sh
//查看脚本执行过程
bash -x a.sh
//查看脚本是否有语法错误
bash -n a.sh
shell脚本的预备知识:date 命令
date命令 是在shell脚本当中最常用的命令,用来显示或者设置系统的日期和时间。
命令行格式: date [参数] [+格式]
参数
含义
-d
–date=字符串:显示指定字符串所描述的时间,而非当前时间
-f
–file=日期文件:类似–date,从日期文件中按行读入时间描述
-r
–reference=文件:显示文件指定文件的最后修改时间
-R
–rfc-2822:以RFC 2822格式输出日期和时间
格式
含义
%y
以两位数字表示年份(00-99)
%Y
以四位数字表示年份
%m
月份(01-12)
%d
按月计的日期(例如:01)
%j
按年计的日期(0-366)
%c
当前locale 的日期和时间 (如:2005年3月3日 星期四 23:05:25)
%b
当前locale 的月名缩写 (如:一,代表一月)
%a
当前locale 的星期名缩写(例如: 日,代表星期日)
%A
当前locale 的星期名全称 (如:星期日)
%H
小时(00-23)
%M
分(00-59)
%S
大写S,表示秒(00-60)
%s
小写s,自UTC 时间 1970-01-01 00:00:00 以来所经过的秒数
%w
小写w,一星期中的第几日(0-6),0 代表周一
%W
大写W,一年中的第几周,以周一为每星期第一天(00-53)
具体用法示例:
显示时间,可以设定显示格式 date + [格式]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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68//显示年
[root@local-linux00 ~]# date +%Y
2018
//用两位数字表示的年份
[root@local-linux00 ~]# date +%y
18
//显示月份
root@local-linux00 ~]# date +%m
07
//显示日期
root@local-linux00 ~]# date +%d
11
[root@local-linux00 ~]# date +%D
07/11/18
[root@local-linux00 ~]# date +%Y%m%d
20180711
[root@local-linux00 ~]# date +%F
2018-07-11
//显示小时
[root@local-linux00 ~]# date +%H
22
//显示分钟
[root@local-linux00 ~]# date +%M
10
//显示秒
[root@local-linux00 ~]# date +%S
19
//时间戳(距离19700101过去多少秒)
[root@local-linux00 ~]# date +%s
1531318421
[root@local-linux00 ~]# date -d @1517637599
Sat Feb 3 13:59:59 HKT 2018
[root@local-linux00 ~]# date +%s -d "2018-02-03 13:59:59"
1517637599
//显示时间
[root@local-linux00 ~]# date +%T
22:14:40
[root@local-linux00 ~]# date +%H:%M:%S
22:14:59
//显示英文月份
[root@local-linux00 ~]# date +%h
Jul
//显示星期几
[root@local-linux00 ~]# date +%w
3
//今年的第几周
[root@local-linux00 ~]# date +%W
28
//显示日历
[root@local-linux00 ~]# cal
July 2018
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
加减操作 -d 参数1
2
3
4
5
6
7date +%Y%m%d //显示前天年月日
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 //显示下一年的日期
一天前的日期:
1date date -d "-1 day" +%d
一小时前:
1date date -d "-1 hour" +%H
一分钟前;
1date date -d "-1 min" +%M
设定时间:-s 选项,
注:设置当前时间,只有root权限才能设置,其他只能查看
1
2
3
4
5
6date -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" //这样可以设置全部时间
编写第一个脚本1
2
3
4
5
6
7
8
9cd /usr/local/sbin
vi a.sh
#!/bin/bash
# first shell script
echo "123"
w
ls
执行脚本效果
查看脚本执行过程
示例:
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
32```
## 脚本变量
变量格式: `var=value` 其中,var是变量名,value是变量内容;
跟其他的编程语言,比如python、javascript了类似,以下情况均考虑使用变量:
* 某个字符串需要频繁使用,且很长;
* 使用条件语句时 `if [ condition ]; then ... ; fi;`
* 引用命令的结果时,用变量替代 `n=wc -l 1.txt`
* 写用户交互脚本,比如 `read -p "Input a number: " n; echo $n` //如果没写这个n,可以直接使用`$REPLY`;
* 内置变量 `$0, $1, $n` 其中,`$0`表示脚本本身,`$1` 第一个参数,`$n`表示参数个数;
* 数学运算a=1;b=2; `c=$(($a+$b))`或者`$[$a+$b]`
示例:
![](https://farm1.staticflickr.com/975/39925240990_c2e22deb07_o.png)
**注:**引用变量时,要用双引号 "" ,否则 $var 会被当作字面量输出;
### 数字运算
**注:**数学计算要用方括号 [] 括起来;同时,引用变量需要加 $
示例:
cat sum1.sh
#! /bin/bash //shebang
get sum of two numbers //注释:求和
m=3
n=5
sum=$[$m+$n]
echo ‘The sum is $sum.’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17### 获取用户输入
**注:**相当于只定义变量,不赋值;value以参数的形式传入。
示例:
这里用到了rea命令,read 命令从键盘读取变量的值,通常用在shell脚本中与用户进行交互的场合。
命令行格式:`read [选项] [参数]` 参数指的就是变量名,可以跟多个变量名;
常用选项| 含义
---|---
-p|“提示信息” :在等待键盘输入时,输出提示信息,方便用户输入
-t|秒数:指定等待时间,防止read命令一直等待用户输入
-s|输入的数据不显示在监视器上(实际上,数据是显示的,只是read命令将文本颜色设置成与背景相同的颜色)
-n|字符数:指定输入的字符数,只要用户输入指定的字符数,该read命令立即执行完毕
#! /bin/bash
Using ‘read’ in shell scripts
read -p “please input a number: “ x
read -p “Input another number: “ y
sum=$[$x+$y]
echo “$x + $y = $sum”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24### 预设变量
shell变量没有数据类型的区别,变量中的值都是以字符串的形式保存的,按照使用环境大概有以下几种:
* 环境变量:用于保存操作系统运行时使用的环境参数
* 位置变量:Bash将传递给脚本的参数保存在位置变量中,以便于在脚本中引用这些参数
* 预定义变量:由系统保留和维护的一组特殊的变量,这些变量通常用于保存程序运行状态等
* 自定义变量:由用户自行定义的变量,可用于用户编写的脚本,多个命令间的值传递等
预设变量|含义
---|---
$0|保存当前程序或脚本的名称
$*|保存传递给脚本或进程的所有参数
$$|当前进程给脚本的PID号
$!|后台运行的最后一个进程的PID号
$?|用于返回上一条命令是否成功执行。如果这个变量的值为0,说明上一个命令正确执行;如果这个变量的值为非0,则说明上一个命令执行有错误
$#|用于保存脚本的参数个数
示例:
#! /bin/bash
This script contains $0
echo “$1 $2 $0” $0指的是脚本的名字
sum=$[$1+$2]
echo “$1 + $2 = $sum”
1
2
3
4
5
6
7
8## shell脚本中的逻辑判断
这里的逻辑判断特指if判断,跟python等其他的编程语言用法类似,大概想到如果满足条件,就执行以下操作。常见的if判断有一下三种形式,复杂程度依次上升。
### if判断常见的三种形式
#### 第一种形式
if 条件 ; then 语句; fi
1
2示例:
[root@local-linux01 ~]# if true; then echo ‘hello’; fi
hello
1
2
3#### 第二种形式:
if 条件;
then
语句;
else
语句;
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15示例:
```shell
#!/bin/bash
# if-practice
a=2
if
[ $a -gt 3 ]
then
ehco 'A is greater than 3.'
else
echo "No, It's not true."
fi
执行效果
1
2[root@local-linux01 sbin]# bash if*.sh
No, It's not true.
第三种形式:1
2
3
4
5
6
7
8
9if 条件;
then
语句;
elif 条件;
then
语句;
else
语句;
fi
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
elif [ $a -gt $b ]
then
echo "a is greater than b"
elif [ $a -lt $b ]
then
echo "a is less than b"
else
echo "None of the condition met"
fi
注意:
if 判断中,expression 和方括号([ ])之间必须有空格,否则会有语法错误;
这里的条件[ expression ],其实返回的是一个布尔值,如果 expression 返回 true,then 后边的语句将会被执行;如果返回 false,就不会执行;
[ ]中不能使用,==,!=,>=,<=这样的符号
判断脚本最后必须以 fi 来结尾闭合 if,否则会报错。
文件目录属性判断表达式
含义
[ -a FILE ]
如果 FILE 存在则为真
[ -b FILE ]
如果 FILE 存在且是一个块文件则返回为真
[ -c FILE ]
如果 FILE 存在且是一个字符文件则返回为真
[ -d FILE ]
如果 FILE 存在且是一个目录则返回为真;
[ -e FILE ]
如果 指定的文件或目录存在时返回为真。
[ -f FILE ]
如果 FILE 存在且是一个普通文件则返回为真。
[ -g FILE ]
如果 FILE 存在且设置了SGID则返回为真。
[ -h FILE ]
如果 FILE 存在且是一个符号符号链接文件则返回为真。(该选项在一些老系统上无效)
[ -k FILE ]
如果 FILE 存在且已经设置了冒险位则返回为真。
[ -p FILE ]
如果 FILE 存并且是命令管道时返回为真。
[ -r FILE ]
如果 FILE 存在且是可读的则返回为真。
[ -s FILE ]
如果 FILE 存在且大小非0时为真则返回为真。
[ -u FILE ]
如果 FILE 存在且设置了SUID位时返回为真。
[ -w FILE ]
如果 FILE 存在且是可写的则返回为真。(一个目录为了它的内容被访问必然是可执行的)
[ -x FILE ]
如果 FILE 存在且是可执行的则返回为真。
[ -O FILE ]
如果 FILE 存在且属有效用户ID则返回为真。
[ -G FILE ]
如果 FILE 存在且默认组为当前组则返回为真。(只检查系统默认组)
[ -L FILE ]
如果 FILE 存在且是一个符号连接则返回为真。
[ -N FILE ]
如果 FILE 存在 and has been mod如果ied since it was last read则返回为真。
[ -S FILE ]
如果 FILE 存在且是一个套接字则返回为真。
[ FILE1 -nt FILE2 ]
如果 FILE1 比 FILE2 新, 或者 FILE1 存在但是 FILE2 不存在则返回为真。
[ FILE1 -ot FILE2 ]
如果 FILE1 比 FILE2 老, 或者 FILE2 存在但是 FILE1 不存在则返回为真。
[ FILE1 -ef FILE2 ]
如果 FILE1 和 FILE2 指向相同的设备和节点号则返回为真。
示例:
1
2
3
4
5
6
7
8
9cat if-file.sh
#!/bin/bash
f="/tmp/if-test"
if [ -f $f ]
then
echo $f exist
else
touch $f
fi
执行效果
1
2
3
4
5[root@local-linux01 sbin]# bash -x !$
bash -x if-file.sh
+ f=/tmp/if-test
+ '[' -f /tmp/if-test ']'
+ touch /tmp/if-test
if特殊用法表达式
含义
[ -z STRING ]
如果STRING的长度为零则返回为真,即空是真
[ -n STRING ]
如果STRING的长度非零则返回为真,即非空是真
[ STRING1 ]
如果字符串不为空则返回为真,与-n类似
示例:
1
2
3
4
5
6
7
8
9
10#!/bin/bash
n=`wc -l /tmp/if-test`
if [ -z "$n" ]
then
echo error
exit
elif [ $n -gt 100 ]
then
echo 'greater than 100'
fi
执行效果
1
2
3
4
5
6
7[root@local-linux01 sbin]# bash -x if03.sh
++ wc -l
++ cat /tmp/if-test
+ n=0
+ '[' -z 0 ']'
+ echo 0
0
case判断
case判断有点像选择题,给你一堆选择,根据你的选择,执行不同的操作。
格式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22case "varname" in
option1)
command
exit
;;
option1)
command
exit
;;
option1)
command
exit
;;
option1)
command
exit
;;
*)
command
exit
;;
esac
注意:
case需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break.
*) 相当于其他语言中的default;除了*)模式,各个分支中;;是必须的,;;相当于其他语言中的break;
示例:
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48#!/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
执行效果
1
2
3
4[root@local-linux01 sbin]# bash !$
bash case01.sh
Please input a number: 1
not ok
执行过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[root@local-linux01 sbin]# bash -x !$
bash -x case01.sh
+ read -p 'Please input a number: ' n
Please input a number: 4
+ '[' -z 4 ']'
++ echo 4
++ sed 's/[0-9]//g'
+ n1=
+ '[' -n '' ']'
+ '[' 4 -lt 60 ']'
+ '[' 4 -ge 0 ']'
+ tag=1
+ case $tag in
+ echo 'not ok'
not ok
shell循环
跟python其他的高级语言类似,shell脚本也有for循环、while循环,以及break、continue。
for循环
格式:
1
2
3for 变量名 in 循环条件;do
command
done
示例1:
1
2
3
4
5#!/bin/bash
for i in `seq 1 100 `
do
echo $i
done
示例2:累加计算
1
2
3
4
5
6
7
8#!/bin/bash
sum=0
for i in `seq 1 100`
do
echo "$sum + $i"
sum=$[$sum+$i]
echo $sum
done
示例3:遍历文件夹
1
2
3
4
5
6
7
8
9
10#!/bin/bash
cd /etc/
for a in ls /etc/
do
if [ -d $a ]
then
echo $a
ls $a
fi
done
while循环
while循环相当于一种特殊的for循环,当满足条件时,会一直执行,直到不再满足条件为止。
格式:
1
2
3
4while condition
do
command
done
如果将condition改为布尔值true,就会变成无限循环,一直执行下去。格式可以写成一下三种形式:
1
2
3
4while :
do
command
done
或者
1
2
3
4while true
do
command
done
或者
1for (( ; ; ))
示例1:
1
2
3
4
5
6
7
8
9
10#!/bin/bash
while true
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
Samples2:在循环过程中需要人为的输入一个数字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#!/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 [ ! -z "$n1" ]
then
echo "you just only input numbers."
continue
fi
break
done
break跳出循环
适用场景:将判断和循环结合起来,符合某些条件时break中断循环。注:break只会影响本次循环,在有多重循环嵌套的情况,break只会影响本层级的循环,它的上层循环不受影响。
samples:
1
2
3
4
5
6
7
8
9
10#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
break
fi
echo $i
done
continue结束本次循环
忽略continue之下的代码,直接进行下一次循环
samples:
1
2
3
4
5
6
7
8
9
10#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
continue
fi
echo $i
done
exit退出整个脚本
跟break不同之处,在于使用exit语句,会直接退出整个shell脚本。
samples:
1
2
3
4
5
6
7
8
9
10#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
exit
fi
echo $i
done
注:运行exit语句,直接退出shell脚本,后面的语句都不执行。
shell中的函数
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
用法:第一种:
1
2
3function function_name {
command
}
第二种:
示例
samples1:定义函数打印参数
1
2
3
4
5
6
7
8#!/bin/bash
function inp(){
echo "The first par is $1"
echo "The second par is $2"
echo "The third par is $3"
echo "the scritp name is $0"
echo "the number of par is $#"
}
samples2:加法函数
1
2
3
4
5#!/bin/bash
sum() {
s=$[$a+$b]
echo $s
}
samples3:显示IP
1
2
3
4
5
6
7#!/bin/bash
ip(){
ifconfig |grep -A1 "$1: "|awk '/inet/ {print $2}'
}
read -p "Please input the eth name: " eth
ip $eth
shell中的数组
在Shell中,用括号来表示数组,数组元素用空格符号分割开。定义数组的一般形式为:
1array_name=(value1 value2... valuen)
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。与C语言类似,shell数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
定义数组第一种:
1array_name=(val0 val1 val2 val3)
第二种:
1
2
3
4
5
6array_name=(
val0
val1
val2
val3
)
第三种:也可以单独定义变量
1
2
3array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
读取数组
格式为:
1${array_name[index]}
samples:
1
2
3
4
5
6
7
8
9
10#!/bin/sh
name=(
Zara
Qadir
Mahnaz
Ayan
Daisy
)
echo "First Index: ${name[0]}"
echo "Second Index: ${name[1]}"
执行效果
1
2
3[root@local-linux01 sbin]# bash array01.sh
First Index: Zara
Second Index: Qadir
获取数组长度
示例:
执行效果
删除数组元素1
2
3
4
5//删除数组
unset arrayName
//删除数组元素
unset arrayName[n]
samples:
1
2
3
4
5
6
7
8
9
10
11
12
13#!/bin/sh
name=(
Zara
Qadir
Mahnaz
Ayan
Daisy
)
echo ${name[*]}
echo "echo delete second name:${name[2]}"
unset name[2]
echo $"The name remains: ${name[*]}"
执行效果
1
2
3
4
5[root@local-linux01 sbin]# vi array01.sh
[root@local-linux01 sbin]# bash array01.sh
Zara Qadir Mahnaz Ayan Daisy
echo delete second name:Mahnaz
The name remains: Zara Qadir Ayan Daisy