如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本

写在前面

这则笔记主要整理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

执行脚本效果

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第1张图片

查看脚本执行过程

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第2张图片

示例:

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

}

第二种:

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第3张图片

示例

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

获取数组长度

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第4张图片

示例:

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第5张图片

执行效果

如何写linux运维脚本,linux运维学习笔记:如何编写shell脚本_第6张图片

删除数组元素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

你可能感兴趣的:(如何写linux运维脚本)