Shell脚本条件判断分支:从基础到实战的完整指南
目录导读
- 引言:为什么条件判断是Shell脚本的核心
- 条件判断的基础:test命令与[]
- if-then-else分支结构详解
- elif多分支与嵌套判断
- case语句:多模式匹配的艺术
- 常见条件表达式与运算符
- 实战案例:自动化运维脚本中的条件分支
- 常见错误与调试技巧
- 结语与问答环节
为什么条件判断是Shell脚本的核心
在自动化运维、系统管理甚至日常开发中,Shell脚本承担着串联命令、处理文件、监控系统和调度任务的重任,而条件判断分支正是让脚本具备“智能决策能力”的关键——没有它,脚本只能逐行执行,无法根据文件是否存在、用户输入、命令返回码等动态情况做出不同反应。

想象一下:你需要一个备份脚本,如果备份目录已存在则直接写入,不存在则先创建,若没有条件分支,脚本将无法灵活适应环境变化,掌握Shell脚本的条件判断,是进阶为高效运维工程师的必经之路。
条件判断的基础:test命令与[]
在Shell中,条件判断的核心是test命令及其简写形式(注意两边必须加空格)。test命令会执行一个逻辑表达式,并根据结果返回退出码(0表示真,非0表示假)。
基础语法示例
if test -f "/etc/passwd"; then
echo "文件存在"
fi
# 等价写法(注意[]内前后空格)
if [ -f "/etc/passwd" ]; then
echo "文件存在"
fi
常见测试类型速查表
| 测试类型 | 表达式示例 | 说明 |
|---|---|---|
| 文件测试 | -f file |
是否为普通文件 |
| 目录测试 | -d dir |
是否为目录 |
| 存在测试 | -e path |
路径是否存在 |
| 字符串相等 | "$a" = "$b" |
字符串相等(注意等号两边空格) |
| 字符串非空 | -n "$str" |
字符串长度不为0 |
| 数值比较 | $a -gt 10 |
大于(-lt小于、-eq等于、-ge大于等于) |
注意:在中使用变量时,强烈建议用双引号包裹:[ "$var" = "value" ],否则当变量为空时会导致语法错误。
if-then-else分支结构详解
这是最基础的分支形式,支持单分支、双分支和多分支。
单分支:if-then
if [ "$USER" = "root" ]; then
echo "当前用户是root,权限充足"
fi
双分支:if-then-else
if [ -d "/data/backup" ]; then
echo "备份目录已存在"
else
mkdir -p /data/backup
echo "已创建备份目录"
fi
高级技巧:命令组合与逻辑运算
使用&&(逻辑与)和(逻辑或)可以在单行内实现简洁的条件执行:
[ -d "/tmp" ] && echo "临时目录存在" || echo "目录不存在"
上述命令等价于:如果目录存在则输出前一句,否则输出后一句,但需注意,这种写法在复杂逻辑中可能产生误判,建议在脚本中使用完整的if结构保证可读性。
elif多分支与嵌套判断
当需要判断多个相互排斥的条件时,使用elif能让代码更清晰。
elif示例:系统资源监控
#!/bin/bash
cpu_load=$(uptime | awk '{print $NF}' | tr -d ',')
if (( $(echo "$cpu_load > 2.0" | bc -l) )); then
echo "CPU负载过高,触发告警"
elif (( $(echo "$cpu_load > 1.0" | bc -l) )); then
echo "CPU负载中等,记录日志"
else
echo "系统负载正常"
fi
说明:浮点数比较需要借助bc命令,或用awk内置比较。
嵌套if的注意事项
if [ -f "/var/log/syslog" ]; then
if [ -r "/var/log/syslog" ]; then
echo "日志文件可读"
else
echo "文件存在但不可读"
fi
else
echo "日志文件不存在"
fi
嵌套时务必使用缩进增强可读性,且不要超过3层,否则建议改用case或函数封装。
case语句:多模式匹配的艺术
当需要根据一个变量的多种可能值执行不同操作时,case比多层elif更简洁、高效。
语法结构
case "$变量" in
模式1)
命令
;;
模式2|模式3)
命令
;;
*)
默认命令
;;
esac
注意双分号表示每个模式块的结束,是通配所有剩余情况。
实战案例:用户输入处理
#!/bin/bash
echo "请选择操作:1-备份 2-恢复 3-退出"
read -r choice
case "$choice" in
1|backup|b)
echo "开始执行备份..."
# 实际备份命令
;;
2|restore|r)
echo "开始执行恢复..."
;;
3|exit|q)
echo "退出程序"
exit 0
;;
*)
echo "无效输入,请输入1-3"
;;
esac
使用分隔多个模式,可以匹配用户的各种输入变体(数字或英文)。
case的高级应用:结合正则
部分Shell(如Bash 4.0+)支持扩展模式匹配:
case "$file" in
*.txt)
echo "文本文件"
;;
*.jpg|*.png|*.gif)
echo "图片文件"
;;
*)
echo "其他类型"
;;
esac
常见条件表达式与运算符
数值比较
| 运算符 | 含义 | 示例 |
|---|---|---|
| -eq | 等于 | [ $a -eq 5 ] |
| -ne | 不等于 | [ $a -ne 5 ] |
| -gt | 大于 | [ $a -gt 5 ] |
| -lt | 小于 | [ $a -lt 5 ] |
| -ge | 大于等于 | [ $a -ge 5 ] |
| -le | 小于等于 | [ $a -le 5 ] |
字符串比较的特殊性
- 使用(不是,虽然某些Shell支持,但POSIX标准推荐)
- 空字符串判断推荐:
[ -z "$var" ](为空真)或[ -n "$var" ](非空真) - 字符串比较时,大于号
>需要转义:[ "$a" \> "$b" ],否则会被重定向解释
逻辑运算符
- 取反,如
[ ! -d /tmp ] -a:逻辑与(不建议使用,推荐&&放在括号外)-o:逻辑或(同上,推荐)现代Shell脚本中,建议用
[ cond1 ] && [ cond2 ]代替[ cond1 -a cond2 ],避免歧义。
双括号的进阶用法
使用(Bash/Zsh支持)可以避免许多转义问题,并支持正则匹配:
[[ "$str" =~ ^[0-9]+$ ]] && echo "纯数字字符串" [[ -f "$file" && -r "$file" ]] && echo "文件存在且可读"
实战案例:自动化运维脚本中的条件分支
案例1:磁盘使用率告警脚本
#!/bin/bash
threshold=80
usage=$(df / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$usage" -ge "$threshold" ]; then
echo "磁盘使用率已达${usage}%,触发告警"
logger -p user.warning "磁盘使用率超阈值:${usage}%"
# 发送邮件(需配置mail命令)
# echo "磁盘告警" | mail -s "Disk Alert" admin@example.com
else
echo "磁盘使用率${usage}%,正常"
fi
案例2:检查服务并自动重启
#!/bin/bash
service_name="nginx"
if systemctl is-active --quiet "$service_name"; then
echo "$service_name 运行正常"
else
echo "$service_name 已停止,尝试重启..."
systemctl restart "$service_name"
sleep 2
if systemctl is-active --quiet "$service_name"; then
echo "重启成功"
else
echo "重启失败,请手动检查"
fi
fi
这里利用systemctl is-active的退出码(0表示活跃)作为条件。
案例3:交互式脚本的输入验证
#!/bin/bash
while true; do
read -p "请输入你的年龄(必须是数字):" age
if [[ "$age" =~ ^[0-9]+$ ]]; then
if [ "$age" -ge 18 ]; then
echo "成年,允许访问"
break
else
echo "未成年,禁止访问"
exit 1
fi
else
echo "输入无效,请重新输入数字"
fi
done
双重条件验证:先判断是否为纯数字,再判断数值范围。
常见错误与调试技巧
常见错误TOP5
- 空格缺失:
[ -f"$file" ]应改为[ -f "$file" ] - 变量未引号:
[ $var = "" ]当var为空时展开成,引发错误 - 比较字符串用
-eq:-eq只能用于数字,字符串比较用 - case中忘记:会导致语法错误或意外的条件穿透
- 过度依赖退出码:如
ls /tmp的退出码只能判断ls成功,不能100%证明目录内容
调试技巧
- 使用
bash -x script.sh逐条跟踪命令执行与变量展开 - 在脚本开头加
set -x开启调试模式,set +x关闭 - 使用
echo "DEBUG: var=$var"在关键位置打印变量值 - 善用
exit 1在发现异常时提前退出,避免连锁错误
结语与问答环节
Shell脚本的条件判断分支是构建自动化逻辑的基石,从简单的if判断到复杂的嵌套case,每一个分支都让脚本更加“智能”,通过本文的全面梳理,你应该已经掌握了:test命令与的用法、if-elif-else多层判断、case模式匹配、各种条件运算符,以及三个真实场景的实战代码。
记住核心原则:保持代码可读性,多用引号,复杂逻辑用函数封装,测试充分后再部署到生产环境。
常见问答
Q1:和有什么区别? A:是POSIX标准,适用于所有Shell;是Bash扩展,支持正则匹配、模式扩展,且无需转义尖括号,在脚本中如果只考虑Bash环境,推荐使用。
Q2:如何判断一个命令是否执行成功?
A:直接使用if command; then,因为命令的退出码就是条件,如if grep "error" log.txt; then。
Q3:多个条件如何组合?
A:用&&(和(或者)组合条件,或在内直接写&&和。[[ -d "$dir" && -w "$dir" ]]。
Q4:case语句能否匹配范围?
A:可以,利用模式通配符,如[0-9]匹配单个数字,[a-z]匹配字母,更复杂的范围建议改用if配合正则。
Q5:为什么我的if判断总是进入else分支?
A:常见原因:变量没用引号导致展开为空、空格缺失、字符串比较用了-eq、文件路径包含空格,先用echo打印关键变量值,再用bash -x跟踪执行路径排查。
本文结论:条件判断是Shell脚本的灵魂,掌握它就能编写出适应各种环境的自动化工具,从今天开始,给每个脚本加入聪明的“大脑”吧!