【Linux从入门到精通】第24篇:流程控制——if-else与case分支

目录

一、引言:当if-else不够用了

[二、if-else 语法完整回顾](#二、if-else 语法完整回顾)

[2.1 三种基本形态](#2.1 三种基本形态)

[2.2 单行写法(命令式风格)](#2.2 单行写法(命令式风格))

[2.3 嵌套if:多层条件判断](#2.3 嵌套if:多层条件判断)

[2.4 if-else的适用场景](#2.4 if-else的适用场景)

三、case语句:多分支匹配的利器

[3.1 基本语法](#3.1 基本语法)

[3.2 用case重写服务管理脚本](#3.2 用case重写服务管理脚本)

[3.3 case支持通配符模式](#3.3 case支持通配符模式)

[3.4 用 | 合并多个匹配项](#3.4 用 | 合并多个匹配项)

[3.5 case运用场景总结](#3.5 case运用场景总结)

四、实战:编写完整的服务管理脚本

脚本设计要点

五、case与if-else的选择指南

六、本篇小结

动手练习

七、下篇预告


一、引言:当if-else不够用了

先看一个需求:写一个脚本,根据用户输入的第一个参数执行不同操作:

bash

复制代码
./script.sh start    → 启动服务
./script.sh stop     → 停止服务
./script.sh restart  → 重启服务
./script.sh status   → 查看状态

if-elif-else实现:

bash

复制代码
#!/bin/bash
if [[ "$1" = "start" ]]; then
    echo "启动服务..."
elif [[ "$1" = "stop" ]]; then
    echo "停止服务..."
elif [[ "$1" = "restart" ]]; then
    echo "重启服务..."
elif [[ "$1" = "status" ]]; then
    echo "查看状态..."
else
    echo "用法: $0 {start|stop|restart|status}"
fi

功能没问题,但每增加一个选项就要加一段elif,代码冗长。这时就该case语句登场了。

二、if-else 语法完整回顾

2.1 三种基本形态

bash

复制代码
# 形态一:单分支
if 条件; then
    命令
fi

# 形态二:双分支
if 条件; then
    命令
else
    命令
fi

# 形态三:多分支
if 条件1; then
    命令1
elif 条件2; then
    命令2
elif 条件3; then
    命令3
else
    命令4
fi

2.2 单行写法(命令式风格)

bash

复制代码
# 只关心"条件成立时执行"
[[ -f /etc/nginx/nginx.conf ]] && echo "配置文件存在"

# 只关心"条件不成立时执行"
[[ -f /etc/nginx/nginx.conf ]] || { echo "配置文件缺失"; exit 1; }

# 三元表达式模拟
[[ $USER = "root" ]] && prefix="#" || prefix="$"
echo "${prefix} 当前用户是 ${USER}"

2.3 嵌套if:多层条件判断

bash

复制代码
#!/bin/bash
if [[ -d "$1" ]]; then
    if [[ -r "$1" ]] && [[ -x "$1" ]]; then
        echo "目录可访问"
    else
        echo "目录存在但权限不足"
    fi
else
    echo "目录不存在"
fi

2.4 if-else的适用场景

  • 条件之间有层级关系(先判断A,再判断B,且B是A的子集)

  • 条件逻辑是非此即彼(成功/失败、存在/不存在)

  • 分支数量不超过3个 时可用if-elif

三、case语句:多分支匹配的利器

3.1 基本语法

bash

复制代码
case 变量 in
    模式1)
        命令1
        ;;
    模式2)
        命令2
        ;;
    模式3|模式4|模式5)
        命令3     # 匹配多个模式,任一个成立都执行
        ;;
    *)
        默认命令
        ;;
esac

语法要点

  • caseesac成对(esac是case的反写)

  • 每个分支必须以;;结束(部分Bash也支持;;&;&

  • *是默认分支(类似else),必须放在最后

  • |用来在一个分支中匹配多个模式

3.2 用case重写服务管理脚本

bash

复制代码
#!/bin/bash
case "$1" in
    start)
        echo "启动服务..."
        ;;
    stop)
        echo "停止服务..."
        ;;
    restart)
        echo "重启服务..."
        ;;
    status)
        echo "查看状态..."
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

相比if-elif的版本,case的优势很明显:

  • 不需要反复写"$1" = "xxx"

  • 视觉上更清晰,一目了然四个分支

  • 多个匹配项可以用|合并(见3.4节)

3.3 case支持通配符模式

case的匹配不是简单的字符串比较,而是模式匹配(通配符风格):

bash

复制代码
#!/bin/bash
read -p "请输入一个字符: " char

case "$char" in
    [0-9])        echo "你输入了一个数字" ;;
    [a-z])        echo "你输入了一个小写字母" ;;
    [A-Z])        echo "你输入了一个大写字母" ;;
    ?)            echo "你输入了一个其他单个字符" ;;
    *)            echo "你输入了多个字符" ;;
esac

这个特性在需要根据模式匹配做分支时特别有用。

3.4 用 | 合并多个匹配项

bash

复制代码
#!/bin/bash
read -p "是否继续?(y/n/yes/no): " answer

case "${answer,,}" in        # ${var,,} 把变量值转成小写
    y|yes)   echo "继续执行..." ;;
    n|no)    echo "已取消"; exit 0 ;;
    *)       echo "请输入 y 或 n" ;;
esac

3.5 case运用场景总结

场景 推荐 理由
服务管理脚本(start/stop/restart) case 经典用法,可读性最好
用户交互选择(y/n、1/2/3/4) case 简洁明了
按模式匹配(数字/字母范围) case if-else不方便处理模式
条件有优先级或依赖关系 if-else 例如必须先判断目录存在才检查权限
只判断true/false if 更直观

四、实战:编写完整的服务管理脚本

把前面学到的变量、条件判断、case整合起来,写一个生产级简单的服务管理脚本:

bash

复制代码
#!/bin/bash
# 服务管理脚本 demo-daemon.sh

SERVICE_NAME="demo"
PID_FILE="/var/run/${SERVICE_NAME}.pid"
LOG_FILE="/var/log/${SERVICE_NAME}.log"

# 启动函数
start_service() {
    # 检查是否已在运行
    if [[ -f "$PID_FILE" ]]; then
        pid=$(cat "$PID_FILE")
        if kill -0 "$pid" 2>/dev/null; then
            echo "服务已在运行 (PID: $pid)"
            return 1
        else
            # PID文件存在但进程不存在,清理残留
            rm -f "$PID_FILE"
        fi
    fi

    # 启动服务(以sleep 3600模拟一个常驻程序)
    echo "正在启动 ${SERVICE_NAME}..."
    nohup sleep 3600 >> "$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    echo "服务已启动 (PID: $!)"
}

# 停止函数
stop_service() {
    if [[ ! -f "$PID_FILE" ]]; then
        echo "服务未在运行"
        return 1
    fi

    pid=$(cat "$PID_FILE")
    if kill "$pid" 2>/dev/null; then
        echo "正在停止服务 (PID: $pid)..."
        sleep 1
        # 如果还没死,强制杀死
        kill -9 "$pid" 2>/dev/null
        rm -f "$PID_FILE"
        echo "服务已停止"
    else
        echo "无法停止服务,可能已经退出"
        rm -f "$PID_FILE"
    fi
}

# 状态检查函数
check_status() {
    if [[ -f "$PID_FILE" ]]; then
        pid=$(cat "$PID_FILE")
        if kill -0 "$pid" 2>/dev/null; then
            echo "服务正在运行 (PID: $pid)"
            return 0
        fi
    fi
    echo "服务已停止"
    return 1
}

# 主流程:根据参数分发
case "$1" in
    start)
        start_service
        ;;
    stop)
        stop_service
        ;;
    restart)
        echo "正在重启 ${SERVICE_NAME}..."
        stop_service
        sleep 2
        start_service
        ;;
    status)
        check_status
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

演示

bash

复制代码
chmod +x demo-daemon.sh
sudo ./demo-daemon.sh start       # 启动服务
sudo ./demo-daemon.sh status      # 查看状态
sudo ./demo-daemon.sh restart     # 重启服务
sudo ./demo-daemon.sh stop        # 停止服务
./demo-daemon.sh                  # 不带参数,显示用法

脚本设计要点

(1)PID文件的作用

/var/run/demo.pid文件记录服务进程的PID。这是Linux守护进程的标准做法:

  • 启动时把$!(最后一个后台进程的PID)写入文件

  • 停止时读取PID文件,kill对应进程

  • 状态检查用kill -0判断进程是否存在(不发信号,仅检查)

(2)返回值约定

它符合Linux命令行惯例:成功返回0,失败返回非0。check_status返回0表示正在运行,1表示已停止。

(3)分支设计

start分支先检查是否已运行(幂等性),stop分支处理PID文件残留的情况,restart简单复用前两个函数避免重复代码。

五、case与if-else的选择指南

特征 选case 选if-else
分支基于精确值匹配
分支基于范围或条件
分支数量3个以上
条件之间有依赖关系
模式匹配(通配符)

总结 :如果变量有2-3个明确取值,优先用case;如果需要复杂的逻辑判断(文件是否存在 + 是否可写 + 大小是否超过阈值),用if

六、本篇小结

if-else:适合条件判断,特别是条件之间有依赖关系(先判断存在再判断权限)。

case语句

  • 语法:case 变量 in 模式) 命令 ;; esac

  • 支持通配符模式匹配(*?[0-9]

  • 多个模式用|合并

  • 最适合上述服务管理脚本的子命令分发

核心差异 :if判断条件是否为真 ,case判断变量匹配哪个模式

动手练习

bash

复制代码
#!/bin/bash
# 练习:编写一个文件类型识别脚本

FILE="$1"

# 检查是否传入参数
if [[ -z "$FILE" ]]; then
    echo "用法: $0 <文件路径>"
    exit 1
fi

# 检查文件是否存在
if [[ ! -e "$FILE" ]]; then
    echo "错误: $FILE 不存在"
    exit 1
fi

# 根据文件扩展名判断类型
case "${FILE##*.}" in
    sh|bash)   echo "Shell脚本" ;;
    py)        echo "Python脚本" ;;
    js)        echo "JavaScript文件" ;;
    html|htm)  echo "HTML文件" ;;
    css)       echo "样式表文件" ;;
    json)      echo "JSON数据文件" ;;
    yaml|yml)  echo "YAML配置文件" ;;
    conf|cfg)  echo "配置文件" ;;
    log)       echo "日志文件" ;;
    gz|bz2|xz) echo "压缩包" ;;
    *)         echo "未知文件类型: .${FILE##*.}" ;;
esac

# 输出文件大小信息
echo "文件大小: $(du -h "$FILE" | cut -f1)"

保存为filetype.sh,测试:

bash

复制代码
chmod +x filetype.sh
./filetype.sh test.py        → Python脚本
./filetype.sh /etc/nginx/nginx.conf → 配置文件

七、下篇预告

掌握了判断和分支,脚本已经能做出"智能决策"。但很多任务需要重复执行------比如遍历所有.log文件、等待某个条件成立、循环处理直到完成。

下一篇我们将进入循环结构的世界,学习:

  • for循环遍历列表和文件

  • while循环读取文件和等待条件

  • breakcontinue控制循环流程

  • 批量重命名、批量处理日志等实战案例


延伸思考case中的;;;;&;&有什么区别?;;匹配后退出case块,;;&继续匹配下一个模式(Bash 4.0+),;&无条件继续执行下一个分支(几乎不用)。这种设计让case具有了"贯穿"能力,但也增加了行为复杂度。日常使用保持;;即可,高级场景再查阅文档。

相关推荐
上海云盾安全满满2 小时前
高防服务器与云防产品都适用哪些情况
运维·服务器
忡黑梨2 小时前
eNSP_登录华为设备
运维·服务器·网络·华为·负载均衡
同聘云2 小时前
谷歌云服务器cdn防御服务器有什么特点??如何搭建cdn防御服务器??
运维·服务器·谷歌云·云服务器·云小强
鸽芷咕2 小时前
KingbaseES NFS部署实战:环境变量缺失与权限报错排查指南
前端·chrome
沉下去,苦磨练!2 小时前
Linux常用指令大全
linux·运维·服务器
加号32 小时前
Nginx 实现负载均衡:从原理到实践的完整指南
运维·nginx·负载均衡
wanhengidc2 小时前
小带宽服务器都有哪些用途
运维·服务器·网络·安全·智能手机
想唱rap2 小时前
TCP套接字编程
java·linux·网络·c++·tcp/ip·mysql·ubuntu
heiqizero2 小时前
spark01-创建RDD
linux·前端·python