Appearance
05|条件判断
前面的脚本里用过几次 if——判断命令成功没、判断参数是不是目录。if 是脚本做决策的基础,但 Shell 的 if 和其他语言不太一样:它判断的不是 true/false,而是命令的退出码。
本篇讲 Shell 条件判断的写法:test、[ ]、[[ ]],以及字符串、数字、文件的判断。
一、if 判断的是退出码
Shell 的 if 后面跟一条命令,命令退出码 0 为真、非 0 为假:
bash
if mkdir /tmp/backup; then
echo "创建成功"
fimkdir 成功返回 0,进 then 分支。这和上一篇的 && 逻辑一致。
如果要在命令失败时做事,加 else:
bash
if mkdir /tmp/backup; then
echo "创建成功"
else
echo "创建失败,可能已存在"
fi二、test 和 [ ]
要判断"变量值是不是等于某个字符串""文件存不存在",用 test 命令或它的另一种写法 [ ]:
bash
# 两种写法等价
test "$user" = "deploy" && echo "是 deploy"
if [ "$user" = "deploy" ]; then
echo "是 deploy"
fi[ 本质是个命令(后面要跟 ] 作参数),所以 [ 和后面的内容之间要有空格。漏空格会报错:
bash
if ["$user" = "deploy"]; then # 错:[ 和 $user 粘在一起字符串判断
bash
[ "$a" = "$b" ] # 相等
[ "$a" != "$b" ] # 不等
[ -z "$a" ] # 空串
[ -n "$a" ] # 非空数字判断
数字比较要用专门的运算符,不能直接用 =(那是字符串比较):
bash
[ "$count" -eq 5 ] # 等于
[ "$count" -ne 5 ] # 不等
[ "$count" -gt 5 ] # 大于
[ "$count" -ge 5 ] # 大于等于
[ "$count" -lt 5 ] # 小于
[ "$count" -le 5 ] # 小于等于文件判断
bash
[ -f /etc/passwd ] # 是普通文件
[ -d /var/log ] # 是目录
[ -e /tmp/lock ] # 存在
[ -r file ] # 可读
[ -w file ] # 可写
[ -x file ] # 可执行
[ -s file ] # 文件非空三、[[ ]]:增强版
[[ ]] 是 bash 的增强判断,比 [ ] 更安全、功能更多。新脚本推荐用 [[ ]]:
bash
if [[ "$user" == "deploy" ]]; then
echo "是 deploy"
fi[[ ]] 的好处:
| 特性 | [ ] | [[ ]] |
|---|---|---|
| 变量不加引号 | 含空格会出错 | 安全 |
== 和 = | == 不一定支持 | 都支持 |
| 通配符匹配 | 不支持 | 支持 |
| 逻辑组合 | -a -o | && || |
通配符匹配是 [[ ]] 独有的:
bash
file="nginx-access.log"
if [[ "$file" == *.log ]]; then
echo "是日志文件"
fi正则匹配用 =~:
bash
if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "是合法的 IP 格式"
fi[[ ]] 里变量引用可以不加引号,但养成加引号的习惯更稳。
四、组合条件
多个条件组合用 &&(与)和 ||(或):
bash
if [[ -d /data && -w /data ]]; then
echo "/data 是目录且可写"
fi
if [[ ! -f /tmp/lock ]]; then
echo "没有锁文件,可以执行"
fi[ ] 里要用 -a(与)和 -o(或),但容易错乱,新脚本用 [[ ]] 配 && || 更清晰。
五、case:多分支
多个值匹配用 case 比 if-elif 清晰:
bash
case "$1" in
start)
systemctl start nginx
;;
stop)
systemctl stop nginx
;;
restart)
systemctl restart nginx
;;
*)
echo "用法: $0 {start|stop|restart}" >&2
exit 1
;;
esaccase 用通配符匹配分支:
bash
case "$file" in
*.log) echo "日志文件" ;;
*.conf) echo "配置文件" ;;
*.tar.gz) echo "压缩包" ;;
*) echo "其他" ;;
esac六、一个完整例子
写个判断磁盘使用率的脚本,把前面学的串起来:
bash
#!/usr/bin/env bash
set -euo pipefail
threshold=${1:-80}
mount=${2:-/}
usage=$(df "$mount" | awk 'NR==2 {gsub(/%/,""); print $5}')
if [[ -z "$usage" ]]; then
echo "错误: 无法获取 $mount 的使用率" >&2
exit 1
fi
if (( usage >= threshold )); then
echo "告警: $mount 使用率 ${usage}% 超过阈值 ${threshold}%"
exit 1
else
echo "正常: $mount 使用率 ${usage}%"
fi(( )) 是算术判断,比 [ ] 的 -ge 写数字比较更直观。df 加 awk 取使用率,gsub 去掉百分号。
学会了之后
条件判断让脚本能根据情况做不同事——参数对不对、文件存不存在、磁盘超没超阈值。[[ ]] 和 case 覆盖了绝大多数判断需求。
下一篇讲循环——遍历文件、批量处理、重复执行。