Shell脚本(英语:Shell script),又称Shell命令稿、程序化脚本,是一种计算机程序与文本文件,内容由一连串的shell命令组成,经由Unix Shell直译其内容后运作。被当成是一种脚本语言来设计,其运作方式与解释型语言相当,由Unix shell扮演命令行解释器的角色,在读取shell脚本之后,依序运行其中的shell命令,之后输出结果。利用shell脚本可以进行系统管理,文件操作等。

Shell脚本就是一种命令型语言,虽然它没有Python,JavaScript,Php这些脚本语言强大,但它拥有一般编程语言有的:变量,作用域,数据类型,逻辑运算符,条件运算符,函数等。

Shell脚本内可以直接使用Linux命令,通过编写Shell脚本可以帮助我们实现自动化运维。在运维工作中Shell脚本发挥巨大的重要。

例如有一个需求,需要每天凌晨3点将现网数据库做备份,并只保留最近三天的现网数据。只需将需要执行的命令写如Shell脚本并设置一个任务计划,系统就会在凌晨3点时自动执行这个脚本文件。

任何一组重复性的Linux操作都应该写成一个Shell脚本!

Hello World!

1
2
#! /bin/bash
echo 'Hello World!'

上面代码保存到hw.sh文件中。执行该脚本的方法是:

1
2
3
$ chmod +x hw.sh
$ ./hw.sh
Hello World

#!是告诉系统应该用那个解释器执行该脚本。#!只能在一个脚本的头部,否则会认为是注释,Shell注释的符号是#

chmod +x给脚本添加执行权限。

./hw.sh告诉系统在执行本目录下下的hw.sh脚本。如果省略了./系统会去$PATH中查找hw.sh

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
name="fynn" #定义一个变量 name

readonly name #将变量 name设置为只读

echo "your name:${name}" #在字符串使用变量

echo ${#name} #输出变量 name长度

echo ${#name:0:2} #截取字符串

echo `expr index "$name" y` #查找子字符串 y的位置

unset name # 删除变量

在Shell脚本中变量命名只能使用:英文字母,数字和下划线。首个字符不能以数字开头。

Shell脚本中" "双引号内可以使用变量(${xxx}),''单引号中无法使用变量。

注意 变量名与=和值之间不能有空格!

传递参数

我们在执行Shell脚本时可以向其传递参数,完成脚本的执行。在Shell内部默认获取外部参数的格式是:$nn表示数字,1为执行脚本的第一个参数,2为第二个参数,以此类推。(0表示脚本名字)

1
2
3
4
#! /bin/bash
echo "名字1:$1"
echo "名字2:$2"
echo "名字3:$3"

执行脚本:

1
$ ./name.sh Fynn Echo SmallMouse

参数之间用空格隔开。

Shell定义了一些特殊字符用于处理参数

参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。如 “$*” 用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$- 显示Shell使用的当前选项,与set命令功能相同
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

数组

Bash Shell只支持一维数组,初始化时也不需要指定数组大小。数组元素开始下标从0开始。

1
2
3
4
5
6
7
8
#! /bin/bash
array_name=('Fynn' 'Echo' 'SmallMouse')

echo array_name[0]
echo array_name[1]
echo array_name[2]
echo "所有用户姓名:${array_name[*]}" # 也可以使用 ${array_name[@]}显示数组所有内容
echo "显示用户姓名数量:${#array_name[*]}" # 也可以使用 ${#array_name[@]}显示数组大小

注意 数组中的值使用空格分隔,变量名与=和值之间不能有空格。

运算符

Shell脚本支持传统的运算符(算法运算符,关系运算符,布尔运算符,字符串运算符,文件测试运算符),但和其它编程语言不一样的时Shell脚本中一些运算符需要通过表达式完成,而这些表达式是用反引号**`**包裹着。

例如,两个数字相加需要使用命令expr

1
2
3
4
#!/bin/bash

val=`expr 2+2`
echo "2+2等于${val}"

算术运算符

下表列出常用的算法运算符,假定变量a为10,而b为20:

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30
- 减法 expr $a - $b 结果为 -10
* 乘法 expr $a * $b 结果为 200
/ 除法 expr $b / $a 结果为 2
% 取余 expr $b % $a 结果为 0
= 赋值 a=$b 将把变量 b 的值赋给 a
== 相等。用于比较两个数字,相同则返回 true [ $a == $b ] 返回 false
!= 不相等。用于比较两个数字,不相同则返回 true [ $a != $b ] 返回 true

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true [ $a -eq $b ] 返回 false
-ne 检测两个数是否不相等,不相等返回 true [ $a -ne $b ] 返回 true
-gt 检测左边的数是否大于右边的,如果是,则返回 true [ $a -gt $b ] 返回 false
-lt 检测左边的数是否小于右边的,如果是,则返回 true [ $a -lt $b ] 返回 true
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true [ $a -ge $b ] 返回 false
-le 检测左边的数是否小于等于右边的,如果是,则返回 true [ $a -le $b ] 返回 true

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true [ $a -lt 20 -a $b -gt 100 ] 返回 false

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
&& 逻辑的AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
or 逻辑的OR [[ $a -lt 100 or $b -gt 100 ]] 返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true [ $a = $b ] 返回 false
!= 检测两个字符串是否相等,不相等返回 true [ $a != $b ] 返回 true
-z 检测字符串长度是否为0,为0返回 true [ -z $a ] 返回 false
-n 检测字符串长度是否为0,不为0返回 true [ -n "$a" ] 返回 true
str 检测字符串是否为空,不为空返回 true [ $a ] 返回 true

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。 file='/var/www/test.sh'

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true [ -b $file ] 返回 false
-c file 检测文件是否是字符设备文件,如果是,则返回 true [ -c $file ] 返回 false
-d file 检测文件是否是目录,如果是,则返回 true [ -d $file ] 返回 false
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true [ -f $file ] 返回 true
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true [ -g $file ] 返回 false
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true [ -k $file ] 返回 false
-p file 检测文件是否是有名管道,如果是,则返回 true [ -p $file ] 返回 false
-u file 检测文件是否是有名管道,如果是,则返回 true [ -u $file ] 返回 false
-r file 检测文件是否可读,如果是,则返回 true [ -r $file ] 返回 true
-w file 检测文件是否可写,如果是,则返回 true [ -w $file ] 返回 true
-x file 检测文件是否可执行,如果是,则返回 true [ -x $file ] 返回 true
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true [ -s $file ] 返回 true
-s file 检测文件(包括目录)是否存在,如果是,则返回 true [ -e $file ] 返回 true

echo命令

echo是Shell中最常用的命令之一,一般用于在屏幕输出信息。

1
2
3
4
5
6
7
8
9
10
11
echo "It is a test"
echo "\"It is a test\"" # 输出特殊字符用 \修饰
read name #read 表示获取用户输入的数据
echo "My name ${name}" # ${name} 可以替换成 $name

echo -e "OK! \n" # 开启转义 \n 表示换行
echo -e "OK! \c" # 开启转义 \c 表示不换行

echo "It is test" > test.txt # 将字符串输入文件 test.txt,如果原文件有内容直接覆盖调,如果是向文件添加内容使用 >>

echo `date` # 显示执行命令的结果

printf命令

printf命令和echo命令作用一样,但它由POSIX标准所定义拥有更好的移植性。

并且它支持格式化字符串,拥有更灵活的展示效果。printf format-string [arguments...]

  • format-string: 为格式控制字符串。 占位符有:%s表示字符串,%f表示浮点数,%d表示数字
  • arguments: 为参数列表

printf自身不带换行如果需要换行需要在字符串末尾添加\necho自身带换行每一个echo会打印在单独一行上。

1
2
3
4
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

执行脚本,输出结果如下:

1
2
3
4
姓名     性别   体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99

%-10s指一个宽度为10个字符,如果字符长度不足10个字符会自动以空格填充。%-4.2f指格式化小数,其中保留2位小数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 #!/bin/bash
# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"

# 没有引号也可以输出
printf %s abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def

printf "%s\n" abc def

printf "%s %s %s\n" a b c d e f g h i j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"

test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

test类似于[]

数值测试

参数 说明
-eq 等于则为真
-ne 不等于则为真
-gt 大于则为真
-ge 大于等于则为真
-lt 小于则为真
-le 小于等于则为真
1
2
3
4
5
6
7
8
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi

代码中的 [] 执行基本的算数运算

1
2
3
4
5
6
7
8
#!/bin/bash

a=5
b=6

result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"
#result 为: 11

字符串测试

参数 说明
= 等于则为真
!= 不相等则为真
-z 字符串 字符串的长度为零则为真
-n 字符串 字符串的长度不为零则为真
1
2
3
4
5
6
7
8
9
num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi
# 两个字符串不相等!

流程控制

流程控制是一个编程语言的基础,Shell脚本拥有常用的:if else,forwhile

if else

if语句有三种状态:if,if..else,if else-if else

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
# if
if condition
then # then 不可省略
command1
command2
...
commandN
fi #fi 不可省略

# if-else
if condition
then
command1
command2
...
commandN
else
command
fi

# if else-if else
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi

实例:

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
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
# a 小于 b

num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo '两个数字相等!'
else
echo '两个数字不相等!'
fi
# 两个数字相等!

for

格式:

1
2
3
4
5
6
7
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done

如果是字符串,for会一一读取字符串中的每个字符赋给变量var

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done

# The value is: 1
# The value is: 2
# The value is: 3
# The value is: 4
# The value is: 5

for str in 'This is a string'
do
echo $str
done
# This is a string

while

while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:

1
2
3
4
while condition
do
command
done

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
# 1
# 2
# 3
# 4
# 5

echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done
# 按下 <CTRL-D> 退出
# 输入你最喜欢的网站名:菜鸟教程
# 是的!菜鸟教程 是一个好网站

case

Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

case工作方式如上所示。取值后面必须为单词in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac # 表示 case结束

输入不同的内容,会有不同的结果,例如:

1
2
3
4
输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3

跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。

break命令允许跳出所有循环(终止执行后面的所有循环)。

continue命令与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
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
# 输入 1 到 5 之间的数字:3
# 你输入的数字为 3!
# 输入 1 到 5 之间的数字:7
# 你输入的数字不是 1 到 5 之间的! 游戏结束

#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done

函数

用户可以自定义函数被脚本随意调用。格式:

1
2
3
4
5
6
7
8
9
10
11
[ function ] funname [()]

{

action;

[return int;]

}
funname # 调用函数
funname 1 2 3 # 传递参数 在函数内部通过 $1 $2 $3 依次获取参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
# 这个函数会对输入的两个数字进行相加运算...
# 输入第一个数字:
# 1
# 输入第二个数字:
# 2
# 两个数字分别为 1 和 2 !
# 输入的两个数字之和为 3 !

函数返回值在调用该函数后通过 $? 来获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
# 第一个参数为 1 !
# 第二个参数为 2 !
# 第十个参数为 10 !
# 第十个参数为 34 !
# 第十一个参数为 73 !
# 参数总数有 11 个!
# 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

注意:$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数
$- 显示Shell使用的当前选项,与set命令功能相同
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误

输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入(获取数据的地方)并将所产生的输出(传递数据的终点位置)发送回到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。

重定向命令列表如下:

命令 说明
command > file 将输出重定向到 file(file是数据最终放置位置,如果file有数据会覆盖调)
command < file 将输入重定向到 file(file是获取数据位置)
command >> file 将输出以追加的方式重定向到 file(将数据最加到文件末尾)
n > file 将文件描述符为 n 的文件重定向到 file
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file
n >& m 将输出文件 m 和 n 合并
n <& m 将输入文件 m 和 n 合并
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入

参考

http://www.runoob.com/linux/linux-shell.html