多读书多实践,勤思考善领悟

Linux Shell脚本编程

本文于1927天之前发表,文中内容可能已经过时。

1. Shell是什么

Shell是用户与内核进行交互操作的一种接口,目前最流行的Shell成为bash Shell。Shell同时也是一门编程语言<解释型的编程语言>,即shell脚本。一个系统可以存在多个shell,可以通过cat /etc/shells命令查看系统中安装的shell,不同的shell可能支持的命令语法是不相同的。

1.1 第一个shell脚本

创建一个文件test.sh,扩展名为sh,即为一个shell脚本。shell脚本的第一行一般为:

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

解析:#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell

​ echo 命令用于向窗口输出文本

1.2 运行Shell脚本的两种方式

① 首先赋予+x权限,然后输入脚本的绝对路径或相对路径

1
2
3
chmod +x ./test.sh       #使脚本具有执行权限
./test.sh #相对路径执行脚本
/root/test.sh #绝对路径执行脚本

② bash或sh + 脚本,不需要赋予x执行权限

1
2
bash /root/helloworld.sh
sh helloworld.sh

2. Shell中的变量

2.1 Shell变量的类型

Linux Shell中的变量分为“系统变量”和“用户自定义变量”。系统变量一般包括$HOME,$PWD,$SHELL,$USER等等。通过set命令可以查看当前shell中所有变量,包括系统变量和用户自定义变量。

2.2 变量的定义与撤销

定义普通变量

语法:变量=值,如

1
STR="adb"

注意:定义变量时,变量名不加$符号。等号两侧不能有空格。双引号和单引号有区别,单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的,转义字符也无效,而双引号可以有变量和转义字符。

定义静态变量

1
readonly B=2

声明静态的变量B=2,不能使用unset撤销静态变量

撤销变量A

1
2
A=9
unset A

全局变量设置

export 变量名 可把变量提升为全局环境变量,可供其他shell程序使用

将命令的返回值赋给变量

1
2
A=`ls -la`
A=$(ls -la)

A=ls -la反引号,运行里面的命令,并把结果返回给变量A。
A=$(ls -la)等价于反引号

2.3 Shell中的特殊变量

$? 表示上一个命令退出的状态
$$ 表示当前进程的编号
$0 表示当前脚本名称
$n 表示n位置的输入参数(n代表数字,n>=1)
$# 表示参数的个数,常用于循环
$*和$@ 都表示参数列表

注意:$与$@区别
① $
和$@都表示传递给函数或脚本的所有参数,不被双引号””包含时,都以$1 $2 … $n的形式输出所有参数
② 当它们被双引号””包含时,”$*”会将所有的参数作为一个整体,以”$1 $2 … $n”的形式输出所有参数;”$@”会将各个参数分开,以”$1” “$2” … “$n”的形式输出所有参数。

3. Shell运算符

Shell支持多种运算符,包括:

算数运算符、关系运算符、布尔运算符、逻辑运算符、字符串运算符、文件测试运算符

3.1 与数值有关的运算符:算数运算符和关系运算符

算数运算符

算数运算符有:

+ - * / % = == !=
取余 赋值 相等 不相等

(+ - * / %)使用格式:expr m + n 或 $((m+n))

注意:原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如awk和expr,expr最常用。expr是一款表达式计算工具,使用它能完成表达式的求值操作。expr运算符间要有空格。(== !=) 条件表达式要放在方括号之间,并且要有空格。

运算符实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#! /bin/bash

val=`expr 2 + 2`
echo "两数之和为:"$val
val2=$((2+2))
echo "两数之和为:"$val2

expr `expr 2 + 3`\*4
echo `expr \`expr 2 + 3\`\*4`
#或
echo $(((2+3)*4))

if [ 10 == 10 ]
then
echo "相等"
fi

注意:乘号(*)、反引号(`)必须加反斜杠()实现转义

关系运算符

关系运算符有

-eq -ne -gt -lt -ge -le
相等判断 不相等判断 大于判断 小于判断 大于等于判断 小于等于判断

关系运算符使用格式: [ $a -eq $b ]

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

a=10
b=20
if [ $a -eq $b ]
then
echo "a等于b"
elif [ $a -gt $b ]
then
echo "a大于b"
else
echo "a不大于b"
fi

3.2 布尔运算符与逻辑运算符

布尔运算符

-o -a
非运算 或运算 与运算

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

a=10
b=20

if [ $a != &b ]
then
echo "a不等于b"
fi
if [ $a -lt 100 -a &b -gt 15 ]
then
echo "a小于100且b大于15"
fi
if [ $a -lt 100 -o &b -gt 15 ]
then
echo "a小于100或b大于15"
fi

逻辑运算符

&& \ \
逻辑的AND 逻辑的OR

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

a=10
b=20

if [[ $a -lt 100 && $b -gt 100 ]]
then
echo "返回 true"
fi

if [[ $a -lt 100 || $b -gt 100 ]]
then
echo "返回 true"
fi

注意:感觉布尔运算符与逻辑运算符除了字符表示不一样,意思是一样的

3.3 字符串运算符

= != -z -n str
检测相等 检测不相等 检测字符串长度为0 检测字符串长度不为0 检测字符串为空

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

a="abc"
b="efg"

if [ $a = $b ]
then
echo "a等于b"
fi

if [ -z &a ]
then
echo "a长度为0"
fi

if [ &a ]
then
echo "字符串不为空"

3.4 文件测试运算符

-r file -w file -x file -f file -s file -d file -b file -L file
有读的权限 有写的权限 有执行的权限 文件存在并且是一个常规的文件 文件存在且不为空 文件存在并且是一个目录 文件存在并且是一个块设备 文件存在并且是一个链接

实例:

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

file="/var/test.sh"
if [ -r $file ]
then
echo "文件可读"
fi

4.Shell流程控制

4.1 if判断

if语句语法格式:

if condition
then
command1
[elif condition
then
command2… ]
[else
command3 ]
fi

示例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
read -p "please input your name:" NAME
#printf '%s\n' $NAME
if [ $NAME = root ]
then
echo "hello ${NAME}, welcome !"
elif [ $NAME = itcast ]
then
echo "hello ${NAME}, welcome !"
else
echo "SB, get out here !"
fi

condition判断语句
格式: condition

#非空返回true,可使用$?验证(0为true, >1为false)如:
[ itcast ]

#空返回false
[ ]

4.2 for循环

第一种写法(常用)

for循环一般格式为:
for var in item1 item2 … itemN
do
command1
done

写成一行:
for var in item1 item2 … itemN; do command1; done;

示例:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
for N in 1 2 3
do
echo $N
done

#或写成一行
for N in 1 2 3; do echo $N; done
#或
for N in {1..3}; do echo $N; done

第二种写法

实例:

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

for ((i = 0; i <= 5; i++))
do
echo "welcome $i times"
done

#或写成一行
for ((i = 0; i <= 5; i++)); do echo "welcome $i times"; done

4.3 while循环

while使用格式:
while condition
do
command
done

示例:

1
2
3
4
5
6
7
#!/bin/sh
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done

注意:Bash let命令,用于执行一个或多个表达式,变量计算中不需要加上$来表示变量。

4.4 case语句匹配

格式:
case 值 in
模式1)
commandN
;;
模式2)
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

注意:esac为作为case的结束标记。每个case分支用右圆括号,用两个分号(;;)表示break。跳出循环break命令和continue命令。break命令允许跳出所有循环(终止执行后面的所有循环)。continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

5. Shell自定义函数

linux shell可以用户定义函数,然后在shell脚本中可以随便调用
shell中函数的定义格式如下:
[ function ] funname [()]
{
action;
[return int;]
}

说明:

  • 1、可以带function fun()定义,也可以直接fun()定义,不带任何参数。
  • 2、函数返回值,只能通过$?系统变量获得,可以显示加:return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)
  • 3、必须在调用函数方法之前,先声明函数,shell脚本是逐行运行,不像其它语言一样先预编译

示例:

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
#! /bin/bash
function fSum()
{
echo $1,$2;
return $(($1+$2));
}
fSum 2 3;
total=$?
echo "total=\$? 第一次调用\$?后的返回结果: "$total
fSum 5 7
total=$?
echo "total=\$?,\$? 两次调用后的返回结果,\$total=$total \$?=$?";

echo "-------------------------------------------"

funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum$anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

6. Shell输入/输出重定向

一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。

命令 说明
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入

注意:文件描述符0通常是标准输入(STDIN),1是标准输出(STDOUT),2是标准错误输出(STDERR)。

输出重定向

重定向一般通过在命令间插入特定的符合来实现。可以选择覆盖或者追加的模式添加内容

1
command1 > file1 或  command1 >> file1

示例:

1
2
3
$ who > users
echo "hello world" > users
echo "hello shell" >> users

输入重定向

输入重定向可以从文件获取输入,语法为:

1
command1 < file1

示例,接着以上示例,统计users文件的行数,如:

1
wc -l < users

输入与输出重定向结合

1
command1 < infile > outfile

说明:同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。

其他重定向

1
2
3
$ command > file 2>&1
#或者
$ command >> file 2>&1

说明:

1、文件描述符0通常是标准输入(STDIN),1是标准输出(STDOUT),2是标准错误输出(STDERR)

2、将 stdout 和 stderr 合并后重定向到 file 

Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
它的基本的形式如下:

1
2
3
command << delimiter
document
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:
结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
开始的delimiter前后的空格会被忽略掉。

示例,在命令行中通过 wc -l 命令计算 Here Document 的行数:

1
2
3
4
5
6
7
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
3 # 输出结果为 3
$

7.设置后台进程

& 放在启动参数后面表示设置此进程为后台进程。默认情况下,进程是前台进程,这时就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们系统将其在后台启动,可以在启动参数的时候加一个‘&’实现这个目的。

8. 脚本调试

两种方式:

  1. 输入命令:sh -vx helloworld.sh
  2. 或者在脚本中增加set -x