Shell脚本编程

脚本运行环境

1.脚本权限

需要当前用户有执行权限, 可以使用如下命令给当前目录下的test.sh添加权限

1
sudo chmod +x ./test.sh   或者 sudo chmod 777 ./test.sh

2.运行方式

1
2
3
4
5
6
# 运行当前脚本, 不可直接使用 test.sh 方式运行
./test.sh

# 选择编译器运行
/bin/sh test.sh
/bin/bash test.sh

若采用第二种方式运行脚本, 脚本第一行的解释器信息可以不写

3.字符编码格式

由于windows(\r\n)和linux(\n)中定义换行方式不同,

导致直接将windows中编写的shell脚本放入linux可能出现执行报错, 解决方式有如下几种

  • vim

    1
    2
    3
    $ vi test.sh
    :set ff # 查看编码: 显示dos
    :set ff=unix # 转换编码为unix
  • sublime编辑器

    1
    2
    点击 [首选项(preferences)->设置(settings)]
    在对象数组中添加: "default_line_ending": "unix",
  • Phpstorm

    1
    点击右下角按钮, 浮现 line seperator,选择LF  (默认是CRLF)
  • linux工具: dos2unix

    1
    2
    $ apt-get install dos2unix
    $ dos2unix ./*.sh

脚本编写

1.指定自执行解释器

1
#!/bin/sh     首行, 指定使用的执行解释器

2.脚本注释简介

1
2
3
4
5
6
7
##-----------------------------------------------
# Created For Project qbweb_linux
# Author: NI9NE
# DateTime: 2020/11/25 11:11
# Email: <ni9ne@outlook.com>
# Usage: 用于每日备份新增加的文件
##-----------------------------------------------

3.脚本命名

linux中脚本扩展名并不影响执行, 以.sh结尾只是为了见名知意

4.简单脚本

1
2
#!/bin/sh
echo 'hello world!'

脚本书写规则

1.变量

1.1 定义/使用变量

在linux中, 变量定义不用加$符号, 使用时才需要

1
2
3
4
#!/bin/sh
string="hello world!"
echo $string # 或者
echo ${string}

同时, 定义变量应该注意以下几点

  • 变量名只能使用英文字母,数字和下划线,首个字符不能以数字开头。

  • 变量名和等号之间不能有空格

  • 不可使用bash里的关键字

    1
    2
    包括: 
    new,open,edit,list,cd,pwd,set,clear,help,quit,ssh,telnet,rlogin,sftp,ftp,disconnect,reconnect 等
1.2 只读变量/删除变量

定义只读变量, 防止后面修改, 可以使用

1
2
3
4
#!/bin/sh
string="hello world!"
readonly string
string="hello shell!" # 脚本报错

删除变量

1
2
3
4
#!/bin/sh
string="hello world!"
unset string
echo ${string} # 无任何输出

2.字符串

2.1 单双引号的区别

定义字符串时, 使用单/双引号均可, 也可以不用引号(这种情况只适用于没有空格的字符)

单引号中的字符都会原样输出,如果需要用到变量, 最好用双引号

1
2
3
4
5
6
7
8
#!/bin/sh
string=hello
string1="hello world!"
string2='hello world!'
combine1='${string1} welcome'
echo ${combine1} # 输出: ${string1} welcome
combine2="${string1} welcome"
echo ${combine2} # 输出: hello world! welcome
2.2 反引号

在shell中, 反引号代表命令替换, 既将一个命令的标准输出插在一个命令行中任何位置, 使用$(..)同理

1
2
3
4
5
#!/bin/sh
string='当前用户为:'`whoami`
# 或者
string='当前用户为:'$(whoami)
echo ${string} # 此时输出 当前用户为:ubuntu

3.注释

3.1 单行注释

# 开头的行就是注释,会被解释器忽略。

3.2 多行注释
1
2
3
4
5
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

4.函数

使用函数, 需要注意, 函数定义必须处于调用点之前, 为方便书写, 通常将函数定义置于脚本开始部分

4.1 定义函数

可以使用如下方法

1
2
3
4
5
6
7
8
#!/bin/bash
function testFunc(){
...
}
# 或者
testFunc(){
...
}
4.2 调用函数

直接输入函数名即可

1
2
#!/bin/bash
testFunc
4.3 返回值

shell脚本中的返回值也可以定义, 但限制为在0-255的数字, 作为函数执行的状态结果

1
2
3
4
5
6
7
#!/bin/bash
testFunc(){
echo '执行函数testFunc'
return 0
}
testFunc
echo $? # 输出0

$? 在shell中代表上一条命令的执行返回结果, 通常0代表执行正确, 非0代表有错误, 可以通过这个验证代码的执行状态

4.4 参数

函数参数并不需要再定义函数时的()中指定, 默认参数按照数字排序, 第一个参数为${1}, 第10个参数为${10}

传入参数只需要调用时, 以空格为分隔符跟在函数名后面即可

1
2
3
4
5
6
#!/bin/bash
testFunc(){
echo '执行函数testFunc'
echo ${1}
}
testFunc '参数1' # 执行结果为输出: 执行函数testFunc /n 参数1

5.流程控制

5.1 if-else

与PHP不同, shell中的流程控制不能为空, 如果else分支没有语句执行,就不要写这个else.

1
2
3
4
5
6
7
if [ conditon ]; then
command1
elif [ conditon ]; then
command2
else
command3
fi

比如下面的判断目录是否存在, 不存在创建的命令

1
2
3
4
5
6
7
#!/bin/bash
if [ ! -d ${local_path} ]; then
echo '目录不存在'
mkdir ${local_path}
else
echo '目录存在,无需创建'
fi
5.2 for循环
1
2
3
4
5
#!/bin/bash
for loop in 1 2 3 4 5
do
echo "The value is: ${loop}"
done
5.4 while语句
1
2
3
while [ condition ]; do
command
done
1
2
3
4
5
6
7
#!/bin/bash
int=1
while [ $int <= 5 ]
do
echo $int
int=`expr ${int} + 1`
done
5.5 until 循环
1
2
3
4
until [ condition ]
do
command
done
1
2
3
4
5
6
7
#!/bin/bash
a=0
until [ ! ${a} -lt 10 ]
do
echo $a
a=`expr ${a} + 1`
done
5.6 case
1
2
3
4
5
6
7
8
case 值 in
模式1)
command1
;;
模式2)
command2
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
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
5.6 break/continue 跳出循环

6.运算符

6.1 算数运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用

1
2
3
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : ${val}"

注意: 运算符之间必须有空格

常用运算符有:

运算符 说明 举例
+ 加法 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 ]

6.2 关系运算符

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

下表列出了常用的关系运算符,假定变量 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。
6.3 布尔运算符

下表列出了常用的布尔运算符,假定变量 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。
6.4 逻辑运算符

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

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR `[[ $a -lt 100
6.5 字符串运算符

下表列出了常用的字符串运算符,假定变量 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。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
6.6 文件测试运算符
操作符 说明 举例
-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 检测文件是否设置了 SUID 位,如果是,则返回 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。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

7.输入输出重定向

命令 说明
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 之间的内容作为输入。
1
2
3
#!/bin/bash
echo `cat test.txt` > test2.txt # 将test.txt内容覆盖test2.txt
echo `cat test.txt` > test3.txt # 将test.txt内容追加test3.txt
7.1不显示执行输出结果
1
$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出”的效果。

如果需要屏蔽输出和错误, 可以这样写

1
$ command > /dev/null 2>&1

注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出