Skip to content

05|条件判断

前面的脚本里用过几次 if——判断命令成功没、判断参数是不是目录。if 是脚本做决策的基础,但 Shell 的 if 和其他语言不太一样:它判断的不是 true/false,而是命令的退出码。

本篇讲 Shell 条件判断的写法:test[ ][[ ]],以及字符串、数字、文件的判断。

一、if 判断的是退出码

Shell 的 if 后面跟一条命令,命令退出码 0 为真、非 0 为假:

bash
if mkdir /tmp/backup; then
    echo "创建成功"
fi

mkdir 成功返回 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:多分支

多个值匹配用 caseif-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
        ;;
esac

case 用通配符匹配分支:

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 写数字比较更直观。dfawk 取使用率,gsub 去掉百分号。

学会了之后

条件判断让脚本能根据情况做不同事——参数对不对、文件存不存在、磁盘超没超阈值。[[ ]]case 覆盖了绝大多数判断需求。

下一篇讲循环——遍历文件、批量处理、重复执行。