Linux Shell 编程

一、Hello World 示例

Shell 脚本示例

hello world 示例

注:脚本通常以 #!/bin/bash 开头,表示使用 /bin/bash 来解释执行脚本;#! 告诉系统,后面的路径指定的是解释该脚本的 Shell 程序。

二、变量

Shell 中的变量可以分为两类:系统变量和用户自定义变量。

  • 系统变量:由操作系统内置,例如 $HOME$PATH$USER
  • 用户自定义变量:由脚本编写者自己定义。

可以使用 set 命令查看当前环境中的所有系统变量。

1. 变量定义

定义规则如下:

1
2
3
4
1) 变量名可以由字母、数字和下划线组成,但不能以数字开头。
2) 等号两侧不能有空格。
3) 不能使用标点符号。
4) 不能使用 Shell 的关键字(可使用 help 命令查看保留关键字)。

变量使用示例如下:

变量定义示例

变量使用示例

2. 将命令返回内容赋值给变量

1
2
3
4
5
# 反引号
date=`date`

# $() 方式
date=$(date)

两种方式都可以把命令结果赋值给变量,推荐使用 $(),可读性和嵌套能力更好。

命令赋值变量

命令赋值方式

3. 位置参数变量

执行命令时通常会带参数,同样,执行 Shell 脚本时也可以传入参数。如果想在脚本中获取执行脚本时输入的参数信息,可以使用位置参数变量。例如:

1
./localvar.sh 6 7

localvar.sh 中可以获取到 67。常见位置参数如下:

1
2
3
4
$n       # n 为数字,$0 表示脚本本身,$1-$9 表示第 1 到第 9 个参数
$* # 表示所有参数,并把它们看作一个整体
$@ # 表示所有参数,但会区分每个参数
$# # 表示参数的个数

位置参数变量

位置参数示例

4. 预定义变量

Shell 内置了一些预定义变量,类似环境变量,但不同的是,环境变量可以被重新定义,而预定义变量通常不能由程序员重新赋值。

1
2
3
$$      # 当前进程的 PID
$! # 后台运行的最后一个进程的 PID
$? # 上一条命令的返回状态,0 表示成功,非 0 表示失败

预定义变量

预定义变量示例

三、运算符

1. $((运算式))$[运算式]

这是 Shell 中最常用的算术运算方式。

算术运算示例

2. expr 运算

1
2
3
4
5
expr 1 + 1
expr 10 - 2
expr 3 \* 2
expr 10 / 3
expr 10 % 3

其中 * 需要转义,否则会被 Shell 当作通配符。

expr 运算

四、条件判断

Shell 中条件判断的基本语法为:

1
[ condition ]

注意:[condition 之间,以及 condition] 之间都要有空格。

  • 条件非空返回 true
  • 可用 $? 验证命令返回值:0 表示成功/真,非 0 表示失败/假。

1. 数字比较

1
2
3
4
5
6
7
=      # 字符串比较
-lt # 小于
-le # 小于等于
-eq # 等于
-gt # 大于
-ge # 大于等于
-ne # 不等于

数字比较判断

2. 字符串比较

1
2
3
4
-z 字符串      # 判断字符串是否为空(为空返回真)
-n 字符串 # 判断字符串是否为非空(非空返回真)
字符串1 == 字符串2 # 判断两个字符串是否相等(相等返回真)
字符串1 != 字符串2 # 判断两个字符串是否不相等(不相等返回真)

3. 按照文件权限判断

1
2
3
4
5
6
-r 文件    # 文件存在且可读
-w 文件 # 文件存在且可写
-x 文件 # 文件存在且可执行
-u 文件 # 文件存在且有 SUID 权限
-g 文件 # 文件存在且有 SGID 权限
-k 文件 # 文件存在且有 SBit 权限

4. 按照文件类型进行判断

1
2
3
4
5
6
7
8
9
10
# 常用
-f 文件 # 文件存在且为普通文件
-e 文件 # 文件存在
-d 文件 # 文件存在且为目录

# 不常用
-L 文件 # 文件存在且为符号链接
-p 文件 # 文件存在且为管道文件
-s 文件 # 文件存在且非空
-S 文件 # 文件存在且为套接字文件

5. 两个文件之间的比较

1
2
3
文件1 -nt 文件2    # 判断文件1的修改时间是否比文件2新
文件1 -ot 文件2 # 判断文件1的修改时间是否比文件2旧
文件1 -ef 文件2 # 判断两个文件是否为同一个 inode,可理解为是否是同一个文件

6. 多重判断

1
2
3
判断1 -a 判断2    # 逻辑与:两者都成立才为真
判断1 -o 判断2 # 逻辑或:至少一个成立即为真
! 判断 # 逻辑非:取反

五、流程控制

1. if 语句

1
2
3
4
5
6
7
if [ 条件判断式 ]
then
程序
elif [ 条件判断式 ]
then
程序
fi

if 语句示例

2. case 语句

1
2
3
4
5
6
7
8
9
10
11
case $变量名 in
"值 1")
执行程序 1
;;
"值 2")
执行程序 2
;;
*)
执行默认程序
;;
esac

case 语句示例

3. for 语句

1
2
3
4
5
6
7
8
9
10
11
# for in
for 变量 in 值1 值2 值3...
do
程序
done

# C 风格 for
for (( 初始值; 循环控制条件; 变量变化 ))
do
程序
done

for 循环示例

$*$@ 的区别如下图:

$* 与 $@ 区别

4. while 语句

1
2
3
4
while [ 条件判断式 ]
do
程序
done

while 循环示例

六、read 获取控制台输入

1
read [参数] [变量]

参数说明:

  • -p:读取输入时显示提示信息。
  • -t:设置等待时间,超时后不再等待并继续执行。

示例:

1
read -p "请输入用户名:" username

read 输入示例

七、Shell 脚本函数

1. 系统函数

系统函数由 Shell 提供,常用的有 basenamedirname

1
2
3
4
5
# 打印文件名(去掉路径),可指定 suffix 去掉后缀
basename [文件路径] [suffix]

# 打印文件所在目录的绝对路径
dirname 文件绝对路径

basename dirname 函数

2. 自定义函数

1
2
3
4
function funname(){
Action;
[return int;]
}
  • return

自定义函数示例

  • return

函数 return 返回

  • 函数参数:

函数参数传递

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

其他常用参数:

1
2
3
4
5
6
7
$#      # 传递到脚本的参数个数
$* # 以一个单字符串显示所有传递给脚本的参数
$$ # 脚本运行的当前进程 ID
$! # 后台运行的最后一个进程的 ID
$@ # 与 $* 相同,但在加引号时会返回每个参数
$- # 显示 Shell 使用的当前选项,与 set 命令功能相同
$? # 显示最后一个命令的退出状态。0 表示正常,其他值表示有错误

注意:所有函数使用前都必须先定义。这意味着函数必须出现在脚本中能够被解析到的位置,解释器首次发现函数之前不能调用它。调用函数时只需要使用函数名即可。

八、MySQL 备份脚本

以备份本博客的全部数据为例:

1. 编写脚本文件

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
#!/bin/bash
# 备份路径
BACK_UP_DIR=/opt/deploy/myblog
# 取当前时间作为备份文件名
DATE_TIME=$(date +%Y%m%d_%H%M%S)

echo '----------开始备份----------'
echo "----备份路径为: $BACK_UP_DIR/$DATE_TIME.tar.gz---------"

# 主机:HOST 用户:USER 密码:PASSWD 数据库:DATABASE
HOST=localhost
USER=****
PASSWD=****
DATABASE=****

# 创建备份目录(不存在则创建)
[ ! -d $BACK_UP_DIR ] && mkdir -p "$BACK_UP_DIR"

# 执行 MySQL 备份命令
mysqldump -u${USER} -p${PASSWD} --host=$HOST $DATABASE > $BACK_UP_DIR/$DATE_TIME.sql

# 进入 /opt/deploy,打包 upload 和 myblog
cd /opt/deploy/
tar -zcvf $DATE_TIME.tar.gz myblog/ upload/

# 备份至 /root/myblogbackup 目录
mv $DATE_TIME.tar.gz /root/myblogbackup/

# 删除不需要的文件
rm -rf /opt/deploy/myblog

# 删除一星期前的文件
find /root/myblogbackup -mtime +7 -name "*.tar.gz" -exec rm -rf {} \;

echo "-------------备份文件成功--------------"

MySQL 备份脚本

2. 添加定时任务,每天执行一次

1
2
3
crontab -e
# 编辑内容如下(每天 0 点备份)
0 0 * * * /***/***/myblogbackup.sh

crontab 定时备份