Linux 运维实战笔记(七):Shell 流程控制与自动化脚本实战
条件测试
条件测试常用语法
在 bash 编程里,条件测试常用的语法如下:
- 语法1:test <判断表达式>,test命令和"<判断表达式>"之间至少有一个空格。
- 语法2:[ <判断表达式> ],和test命令的用法相同,这是推荐方法。
[]的边界和内容之间至少有一个空格。 - 语法3:[[ <判断表达式> ]],是比test和[]更新的语法格式。
[[]]的边界和内容之间至少有一个空格。 - 语法4:(( <判断表达式> )),一般用于 if 语句。
(())(双小括号)两端不需要有空格。 - 语法5:comand,命令的返回值确定表达式的真值或假值。
条件测试常用于判断以下内容:
- 字符串
- 数值
- 文件、进程、服务、用户等
test
帮助信息
[root@CentOS ~ 10:06:17]# help test
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR. Expressions may be unary or binary. Unary
expressions are often used to examine the status of a file. There
are string operators and numeric comparison operators as well.
The behavior of test depends on the number of arguments. Read the
bash manual page for the complete specification.
File operators:
-a FILE True if file exists.
-b FILE True if file is block special.
-c FILE True if file is character special.
-d FILE True if file is a directory.
-e FILE True if file exists.
-f FILE True if file exists and is a regular file.
-g FILE True if file is set-group-id.
-h FILE True if file is a symbolic link.
-L FILE True if file is a symbolic link.
-k FILE True if file has its `sticky' bit set.
-p FILE True if file is a named pipe.
-r FILE True if file is readable by you.
-s FILE True if file exists and is not empty.
-S FILE True if file is a socket.
-t FD True if FD is opened on a terminal.
-u FILE True if the file is set-user-id.
-w FILE True if the file is writable by you.
-x FILE True if the file is executable by you.
-O FILE True if the file is effectively owned by you.
-G FILE True if the file is effectively owned by your group.
-N FILE True if the file has been modified since it was last read.
FILE1 -nt FILE2 True if file1 is newer than file2 (according to
modification date).
FILE1 -ot FILE2 True if file1 is older than file2.
FILE1 -ef FILE2 True if file1 is a hard link to file2.
String operators:
-z STRING True if string is empty.
-n STRING
STRING True if string is not empty.
STRING1 = STRING2
True if the strings are equal.
STRING1 != STRING2
True if the strings are not equal.
STRING1 < STRING2
True if STRING1 sorts before STRING2 lexicographically.
STRING1 > STRING2
True if STRING1 sorts after STRING2 lexicographically.
Other operators:
-o OPTION True if the shell option OPTION is enabled.
-v VAR True if the shell variable VAR is set
! EXPR True if expr is false.
EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
Arithmetic binary operators return true if ARG1 is equal, not-equal,
less-than, less-than-or-equal, greater-than, or greater-than-or-equal
than ARG2.
Exit Status:
Returns success if EXPR evaluates to true; fails if EXPR evaluates to
false or an invalid argument is given.
判断文件
判断文件是否存在
[root@CentOS ~ 09:34:38]# file=~/home/tz/tz.txt
# 如果文件存在,则显示文件存在
[root@CentOS ~ 09:39:49]# test -a $file && echo "$file is exist." || echo "$file is not exist."
# 输出内容如下
/root/home/tz/tz.txt is exist.
[root@CentOS ~ 09:40:39]# file=~/home/tz/tz1.txt
# 如果文件不存在,则显示文件不存在
[root@CentOS ~ 09:41:11]# test -a $file && echo "$file is exist." || echo "$file is not exist."
# 输出内容如下
/etc/fstab-abc is not exist.
判断目录是否存在:
- 如果不存在,则创建目录。
- 如果存在,则提示目录存在。
[root@CentOS ~ 09:42:37]# dir_path=~/home/tz/webapp
[root@CentOS ~ 09:43:06]# ls ${dir_path}
ls: 无法访问/root/home/tz/webapp: 没有那个文件或目录
# 执行完成后,创建了目录
[root@CentOS ~ 09:43:13]# test -d ${dir_path} && echo "${dir_path} is exist." || mkdir ${dir_path}
# 提示目录已经存在
[root@CentOS ~ 09:43:21]# test -d ${dir_path} && echo "${dir_path} is exist." || mkdir ${dir_path}
/root/home/tz/webapp is exist.
其他示例
# 如果对文件具有读取权限,则显示文件内容
[root@CentOS ~ 09:47:53]$ ls -ld /etc/fstab /etc/shadow
-rw-r--r--. 1 root root 541 Apr 3 14:10 /etc/fstab
----------. 1 root root 690 Apr 3 14:15 /etc/shadow
# 无读取权限,输出内容为空
[root@CentOS ~ 09:48:10]$ test -r /etc/shadow && tail -5 /etc/shadow
# 有读取权限,输出内容
[root@CentOS ~ 09:48:05]$ test -r /etc/fstab && tail -5 /etc/fstab
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=8c0568e1-c35f-49d2-9f4c-e63e76dc1473 /boot xfs defaults 0 0
/dev/mapper/centos-home /home xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
思考:对于文件/tmp/test.log,如果有写入权限,则写入"hello world."
[root@CentOS ~ 10:04:18]$ touch /tmp/test.log
[root@CentOS ~ 10:04:39]$ test -w /tmp/test.log && echo "hello world." > /tmp/test.log
[root@CentOS ~ 10:04:46]$ cat /tmp/test.log
hello world.
判断字符串
- -z,字符串为空,则为true
- -n,字符串非空,则为true
# 测试变量是否定义
[root@CentOS ~ 10:06:17]# test -z "$testvar" && echo 'testvar is not defined.' || echo "testvar is $testvar."
testvar is not defined.
[root@CentOS ~ 10:13:16]# testvar=hi
[root@CentOS ~ 10:13:18]# test -z "$testvar" && echo 'testvar is not defined.' || echo "testvar is $testvar."
# 输出变量值
testvar is hi.
# 取消变量定义
[root@CentOS ~ 10:13:24]# unset testvar
[root@CentOS ~ 10:13:41]# test -n "$testvar" && echo "testvar is $testvar." || \
> testvar=zhangsan
# 再次打印,显示变量值
[root@CentOS ~ 10:14:13]# test -n "$testvar" && echo "testvar is $testvar." || testvar=zhangsan
testvar is zhangsan.
- STRING1 = STRING2,True if the strings are equal.
- STRING1 != STRING2,True if the strings are not equal.
# 检查当前运行的用户
# 如果不是root用户,则提示:请以root身份运行
# 如果是root用户,则执行创建用户zhangsan
# 普通用户运行,则提示:请以root身份运行
[tz@CentOS root 10:33:18]$ test "$USER" = "root" && useradd zhangsan || echo "Please run as root."
Please run as root.
# root用户运行,创建用户成功
[root@CentOS ~ 10:34:31]# test "$USER" = "root" && useradd zhangsan || echo "Please run as root."
[root@CentOS ~ 10:35:06]# id zhangsan
uid=1001(zhangsan) gid=1001(zhangsan) 组=1001(zhangsan)
# 等效于以下命令
[root@CentOS ~ 10:35:08]# test "$USER" != "root" && echo "Please run as root." || useradd zhangsan
Please run as root.
判断数值
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne, -lt, -le, -gt, or -ge.
# 检查当前运行的用户
# 如果不是root用户,则提示:请以root身份运行
# 如果是root用户,则执行删除用户zhangsan
# 普通用户运行,则提示:请以root身份运行
[tz@CentOS root 10:35:45]$ test $UID -eq 0 && userdel -r zhangsan || echo "Please run as root."
Please run as root.
# root用户运行,删除用户成功
[root@CentOS ~ 10:36:29]# test $UID -eq 0 && userdel -r zhangsan || echo "Please run as root."
[root@CentOS ~ 10:36:34]# id zhangsan
id: zhangsan: no such user
多条件测试
- ! EXPR True if expr is false.
- EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
- EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
[root@CentOS ~ 11:25:06]# file=/tmp/test.log
# 逻辑非:提交取反满足
[root@CentOS ~ 11:38:33]# [ ! -a $file ] && touch $file && echo "create file success."
create file success.
[root@CentOS ~ 11:39:08]# ls $file
/tmp/test.log
# 逻辑与:两个条件同时满足
[root@CentOS ~ 11:39:24]# [ -w $file -a -a $file ] && date > $file
[root@CentOS ~ 11:39:36]# cat $file
2026年 04月 13日 星期一 11:39:36 CST
# 逻辑或:两个条件任一满足
[root@CentOS ~ 11:39:42]# file1=/etc/yum.conf
[root@CentOS ~ 11:40:44]# file2=/etc/yum.repos.d/CentOS-Base.repo
[root@CentOS ~ 11:40:50]# [ -a $file1 -o -a $file2 ] && echo 'yum repository is exist.'
yum repository is exist.
[[ ]] 条件测试
大部分等效于 [],多条件判断有些区别。
- ! EXPR True if expr is false.
- EXPR1 && EXPR2 True if both expr1 AND expr2 are true.
- EXPR1 || EXPR2 True if either expr1 OR expr2 is true.
[root@CentOS ~ 11:40:56]# file=/tmp/test.log
# 逻辑非:提交取反满足
[root@CentOS ~ 11:42:26]# [[ ! -a $file ]] && touch $file && echo "create file success."
create file success.
[root@CentOS ~ 11:42:28]# ls $file
/tmp/test.log
# 逻辑与:两个条件同时满足
[root@CentOS ~ 11:43:02]# [[ -w $file && -a $file ]] && date > $file
[root@CentOS ~ 11:43:13]# cat $file
2026年 04月 13日 星期一 11:43:13 CST
# 逻辑或:两个条件任一满足
[root@CentOS ~ 11:43:18]# file1=/etc/yum.conf
[root@CentOS ~ 11:43:32]# file2=/etc/yum.repos.d/CentOS-Base.repo
[root@CentOS ~ 11:43:42]# [[ -a $file1 || -a $file2 ]] && echo 'yum repository is exist.'
yum repository is exist.
(()) 条件测试
主要用于数值比较。
# 检查当前运行的用户
# 如果不是root用户,则提示:请以root身份运行
# 如果是root用户,则执行删除用户zhangsan
# 普通用户运行,则提示:请以root身份运行
[tz@CentOS root 11:44:16]$ (($UID == 0)) && userdel -r zhangsan || echo "Please run as root."
Please run as root.
# root用户运行,删除用户成功
[root@CentOS ~ 11:45:16]# (($UID == 0)) && userdel -r zhangsan || echo "Please run as root."
[root@CentOS ~ 11:45:17]# id zhangsan
id: zhangsan: no such user
判断表达式总结
| 判断表达式符号 | [] | test | [[]] | (()) |
|---|---|---|---|---|
| 边界是否需要空格 | 需要 | 需要 | 需要 | 不需要 |
| 逻辑操作符 | !、-a、-o | !、 -a、 -o | !、&&、|| | !、&&、|| |
| 整数比较操作符 | -eq、-ne、-gt、-ge、-lt、-le | -eq、-ne、-gt、-ge、-lt、-le | -eq、-ne、-gt、-ge、-lt、-le | ==、!=、>、>=、<、<= |
| 字符串比较操作符 | =、== 、!= | =、== 、!= | =、== 、!= | = 、!= |
| 是否支持通配符匹配 | 不支持 | 不支持 | 支持(=~) | 不支持 |
IF 语句
语法
单分支语法
# 单分支
if 条件判断;then
command
fi
if 条件判断
then
command
fi
条件判断 && command
双分支语法
# 单分支
if 条件判断;then
command1
else
command2
fi
条件判断 && command1 || command2
多分支语法
# 单分支
if 条件1判断;then
command1
elif 条件2判断;then
command2
else
command3
fi
实践
示例1:单分支
需求:如果sshd服务正常运行,则提示服务正在运行。
开发脚本if_sshd_1.sh ,内容如下:
[root@centos7 bin 13:53:41]# vim if_ssh_1.sh
#!/bin/bash
status="$(systemctl is-active sshd)"
if [ "$status" == "active" ];then
echo sshd is running.
fi
或者
#!/bin/bash
systemctl is-active sshd &>/dev/null
if (($?==0));then
echo sshd is running.
fi
运行结果:
[root@CentOS bin 13:46:42]# systemctl start sshd
[root@CentOS bin 13:46:46]# bash if_sshd_1.sh
sshd is running.
示例2:双分支
需求:
- 如果sshd服务正常运行,则提示服务正在运行
- 提示服务未运行,并启动服务,启动后提示服务正常运行。
开发脚本if_sshd_2.sh ,内容如下:
[root@centos7 bin 13:43:41]# vim if_ssh_2.sh
#!/bin/bash
status="$(systemctl is-active sshd)"
if [ "$status" == "active" ];then
echo sshd is running.
else
systemctl start sshd && echo "Start sshd success."
fi
运行结果:
[root@CentOS bin 13:50:44]# systemctl stop sshd
[root@CentOS bin 13:51:29]# bash if_sshd_2.sh
Start sshd success.
[root@CentOS bin 13:51:33]# bash if_sshd_2.sh
sshd is running.
示例3:多分支
需求:开发if_sshd_3.sh脚本。
- 脚本使用方法:“Usage:$0 start|stop|status|restart|reload”
- 如果参数数量个数不是1个,则输出脚本使用方法。
- 如果$1是start,则执行’systemctl start sshd’
- 如果$1是stop,则执行’systemctl stop sshd’
- 如果$1是status,则执行’systemctl status sshd’
- 如果$1是restart,则执行’systemctl restart sshd’
- 如果$1是reload,则执行’systemctl reload sshd’
- 如果$1是其他子命令,则输出脚本使用方法。
开发脚本if_sshd_3.sh ,内容如下:
[root@CentOS bin 14:12:36]# vim if_sshd_3.sh
#!/bin/bash
service_name=sshd
if (($#!=1));then
echo "Usage:$0 start|stop|status|restart|reload"
exit
fi
if [ "$1" == "start" ];then
systemctl start ${service_name}
elif [ "$1" == "stop" ];then
systemctl stop ${service_name}
elif [ "$1" == "status" ];then
systemctl status ${service_name}
elif [ "$1" == "restart" ];then
systemctl restart ${service_name}
elif [ "$1" == "reload" ];then
systemctl reload ${service_name}
else
echo "Usage:$0 start|stop|status|restart|reload"
fi
运行结果:
[root@CentOS bin 14:15:42]# chmod +x if_sshd_3.sh
# 缺乏参数
[root@CentOS bin 14:15:55]# if_sshd_3.sh
Usage:/usr/bin/if_sshd_3.sh start|stop|status|restart|reload
# 只能传递1个参数
[root@CentOS bin 14:16:03]# if_sshd_3.sh start stop
Usage:/usr/bin/if_sshd_3.sh start|stop|status|restart|reload
# 传递正常参数
[root@CentOS bin 14:17:00]# if_sshd_3.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 一 2026-04-13 13:54:15 CST; 22min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 31661 (sshd)
Tasks: 1
CGroup: /system.slice/sshd.service
└─31661 /usr/sbin/sshd -D
4月 13 13:54:15 CentOS.tz.cloud systemd[1]: Starting OpenSSH server daemon...
4月 13 13:54:15 CentOS.tz.cloud sshd[31661]: Server listening on 0.0.0.0 port 22.
4月 13 13:54:15 CentOS.tz.cloud sshd[31661]: Server listening on :: port 22.
4月 13 13:54:15 CentOS.tz.cloud systemd[1]: Started OpenSSH server daemon.
Hint: Some lines were ellipsized, use -l to show in full.
# 传递不存在的参数
[root@CentOS bin 14:17:09]# if_sshd_3.sh haha
Usage:/usr/bin/if_sshd_3.sh start|stop|status|restart|reload
示例4:综合练习
开发脚本servicectl,模拟systemctl 控制服务。
不带注释
#!/bin/bash
service_name="$2"
action="$1"
if (($#!=2));then
echo "Usage:$0 start|stop|status|restart|reload ${service_name}"
exit
fi
if [ "$action" == "start" -o "$action" == "stop" -o "$action" == "status" -o "$action" == "restart" -o "$action" == "reload" ];then
systemctl $action ${service_name}
else
echo "Usage:$0 start|stop|status|restart|reload"
fi
带注释
#!/bin/bash
# 功能:通用 Linux 服务管理脚本
# 用法:./脚本名 动作 服务名
# 示例:./servicectl start nginx
# ====================== 变量定义 ======================
# 第一个参数:要执行的动作(start/stop/restart/status/reload)
action="$1"
# 第二个参数:要操作的服务名称(nginx、sshd、mysql 等)
service_name="$2"
# ====================== 参数个数校验 ======================
# 判断参数是否为 2 个,不是则提示用法并退出
if (($# != 2)); then
echo "用法:$0 start|stop|status|restart|reload 服务名"
exit 1
fi
# ====================== 动作合法性判断 ======================
# 判断用户输入的动作是否是合法的系统服务命令
if [ "$action" == "start" -o "$action" == "stop" -o "$action" == "status" -o \
"$action" == "restart" -o "$action" == "reload" ]; then
# 执行 systemctl 命令管理服务
systemctl $action ${service_name}
else
# 动作不合法,提示正确用法
echo "错误:不支持的操作!"
echo "用法:$0 start|stop|status|restart|reload 服务名"
exit 1
fi
FOR 语句
语法
shell 自带语法
for 变量名 in 清单
do
command
done
C语言格式语法
for ((num=1;num<=10;num++))
do
echo $num
done
执行效果如下
1
2
3
4
5
6
7
8
9
10
实践
示例1:计算1+2+…+9+10的和
[root@CentOS bin 15:14:26]# vim for_1_sum.sh
#!/bin/bash
sum=0
for num in {1..10}
do
sum=$[ sum + num ]
done
echo "1+2+..+9+10=$sum"
示例2:批量重命名文件
具体需求:将某个目录下文件命中字符串snap替换为pic。
模拟:
[root@CentOS bin 15:17:24]# mkdir Pictures
[root@CentOS bin 15:48:51]# touch Pictures/snap-{01..10}.jpg
for_2_rename.sh 代码如下:
[root@centos7 bin 15:37:16]# vim for_2_rename.sh
#!/bin/bash
for file in Pictures/snap*
do
file_old_name=$file
file_new_name=${file_old_name/snap/pic}
mv ${file_old_name} ${file_new_name}
done
执行效果如下:
[root@CentOS bin 15:51:06]# bash for_2_rename.sh
[root@CentOS bin 15:51:18]# ls Pictures/
pic-01.jpg pic-03.jpg pic-05.jpg pic-07.jpg pic-09.jpg
pic-02.jpg pic-04.jpg pic-06.jpg pic-08.jpg pic-10.jpg
思考:将某个目录下所有文件备份到/backup目录,文件名添加.bak后缀。
示例3:批量创建用户
公司新入职一批员工,需要为这些员工创建账号。
- 员工姓名清单保存在staff.txt中,每行一个。
- 为每个员工创建一个初始密码,初始密码是8位随机值,要求登录后必须修改密码。
- 创建的结果要保存到users_info.conf中,格式为username: password。
准备员工姓名清单:
[root@CentOS bin 16:19:12]# vim staff.txt
jack
tom
zhangsan
laowang
脚本内容如下:
#!/bin/bash
staff_name_file=staff.txt
staff_info_file=users_info.conf
if [ -a ${staff_name_file} -a -r ${staff_name_file} ];then
for staff in $(cat ${staff_name_file})
do
useradd $staff
password=$(date +%N | md5sum | head -c10)
echo $password | passwd --stdin $staff &>/dev/null
echo "$staff: $password" >> ${staff_info_file}
done
else
echo "${staff_name_file} is not exist."
fi
执行效果如下:
[root@CentOS bin 16:29:14]# bash for_3_users.sh
[root@CentOS bin 16:29:21]# cat users_info.conf
jack: bce575ef4b
tom: 6db89d5834
zhangsan: ee4b67c847
laowang: e2e2136347
生成随机值常见方法:
- 利用时间生成纳秒级别值:
date +%N
[root@centos7 bin 16:04:25]# date +%N
456812617
- mktemp命令创建一个临时目录,目录名是随机值。
[root@centos7 bin 16:05:14]# echo $(mktemp)
/tmp/tmp.ncZkKK9TYD
- 利用环境变量RANDOM生成0~32767随机值。
总结:$() $[] ${}
- $() :命令替换
- $[]:数值计算
- ${}:变量替换
while/until 语句
条件满足或者不满足,一直运行。
语法
while
条件满足,一直运行。
while 条件判断
do
command
done
until
条件不满足,一直运行。满足条件,则终止运行。
until 条件判断
do
command
done
实践
示例1:挣1个小目标
while_1_target.sh 脚本内容如下:
#!/bin/bash
target=100000000
money=100
while ((money<target))
do
echo -n "I'm working hard ... "
sleep 1
money=$[ money+10000000 ]
echo $money
done
执行结果:
[root@centos7 bin 16:49:08]# bash while_1_target.sh
I'm working hard ... 10000100
I'm working hard ... 20000100
I'm working hard ... 30000100
I'm working hard ... 40000100
I'm working hard ... 50000100
I'm working hard ... 60000100
I'm working hard ... 70000100
I'm working hard ... 80000100
I'm working hard ... 90000100
I'm working hard ... 100000100
示例 2:监控 sshd 服务
开发脚本monitor_sshd.sh 实时监控sshd服务,实现以下功能:
- 如果sshd服务没有运行,则报告sshd服务状态到/tmp/sshd_status.log日志;同时启动sshd服务,启动的结果也写入/tmp/sshd_status.log日志。
- 如果sshd服务正常运行,则报告sshd服务状态,并写入/tmp/sshd_status.log日志
- 每隔3秒执行一次监控。
monitor_sshd.sh 脚本内容如下:
#!/bin/bash
log_file=/tmp/sshd_status.log
while true
do
status=$(systemctl is-active sshd)
if [ "$status" == "active" ];then
echo "$(date): sshd status is running." | tee -a ${log_file}
else
echo "$(date): sshd status is not running." | tee -a ${log_file}
systemctl start sshd &>/dev/null && \
echo "$(date): Start sshd success." |tee -a ${log_file} || \
echo "$(date): Start sshd fail." |tee -a ${log_file}
fi
sleep 3
done
执行效果如下:
[root@CentOS bin 17:11:34]# bash monitor_sshd.sh
2026年 04月 13日 星期一 17:11:42 CST: sshd status is running.
2026年 04月 13日 星期一 17:11:45 CST: sshd status is running.
2026年 04月 13日 星期一 17:11:48 CST: sshd status is running.
...
新开终端关闭sshd服务,监控日志变化
[root@CentOS ~ 15:55:51]# systemctl stop sshd
日志内容刷新如下:
2026年 04月 13日 星期一 17:12:41 CST: sshd status is running.
2026年 04月 13日 星期一 17:12:44 CST: sshd status is not running.
2026年 04月 13日 星期一 17:12:44 CST: Start sshd success.
2026年 04月 13日 星期一 17:12:47 CST: sshd status is running.
2026年 04月 13日 星期一 17:12:50 CST: sshd status is running.
更多推荐

所有评论(0)