Shell脚本如何实现条件判断分支

wen 实用脚本 1

Shell脚本条件判断分支:从基础到实战的完整指南

目录导读

  1. 引言:为什么条件判断是Shell脚本的核心
  2. 条件判断的基础:test命令与[]
  3. if-then-else分支结构详解
  4. elif多分支与嵌套判断
  5. case语句:多模式匹配的艺术
  6. 常见条件表达式与运算符
  7. 实战案例:自动化运维脚本中的条件分支
  8. 常见错误与调试技巧
  9. 结语与问答环节

为什么条件判断是Shell脚本的核心

在自动化运维、系统管理甚至日常开发中,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

  1. 空格缺失[ -f"$file" ] 应改为 [ -f "$file" ]
  2. 变量未引号[ $var = "" ] 当var为空时展开成,引发错误
  3. 比较字符串用-eq-eq只能用于数字,字符串比较用
  4. case中忘记:会导致语法错误或意外的条件穿透
  5. 过度依赖退出码:如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脚本的灵魂,掌握它就能编写出适应各种环境的自动化工具,从今天开始,给每个脚本加入聪明的“大脑”吧!

抱歉,评论功能暂时关闭!