Bash 零基础教程

Bash case 语句

Bash case 语句提供了一种优雅的方式来处理多个条件分支。当需要将单个变量或表达式与多个可能的模式进行匹配时,它比冗长的 if-elif-else 结构更简洁、更易读。

这种控制流语句会对表达式进行一次求值,然后将其值与一系列模式进行对比。一旦找到匹配项,就会执行相应的命令块。

1. 理解 case 语句语法

Bash 中 case 语句的基础语法以 case 关键字开头,后跟要评估的变量或表达式,接着是 in 关键字。

然后列出一系列模式(Patterns),每个模式后面跟一个右括号 )、一个命令块,以及两个分号 ;; 来标记该命令块的结束。整个 case 代码块以 esac(即 case 的反写)作为结束标志。

case 表达式 in
    模式1)
        模式1_需要执行的命令
        ;;
    模式2)
        模式2_需要执行的命令
        ;;
    模式3 | 模式4) # 可以使用 | (或) 符号来分隔多个模式
        模式3或模式4_需要执行的命令
        ;;
    *) # 默认情况 (通配符匹配,类似于 if-elif-else 中的 'else')
        默认需要执行的命令
        ;;
esac

核心语法解析:

  • 表达式 (EXPRESSION): 通常是一个变量,它的值将被用来与下方的各个模式进行匹配。
  • 模式 (PATTERN): 这些是字符串或 Shell 通配符(如 *?[]),用于与表达式进行比较。第一个与表达式匹配成功的模式,将触发其关联命令的执行。
  • 命令 (COMMANDS): 如果表达式与前面的模式匹配,则执行的一个或多个命令。
  • ;; (双分号): 标志着特定模式下命令块的结束。如果没有它,case 语句会发生“贯穿(fall through)”现象,继续执行下一个模式的命令(即使下一个模式不匹配)。这通常不是我们期望的结果(尽管在高级流程控制中偶尔会故意这么用)。
  • | (管道符): 允许你为同一个命令块指定多个模式。如果表达式匹配由 | 分隔的任何一个模式,就会执行关联的命令。
  • * (星号): 作为一个通配符,可以匹配任何字符串。它通常被用作最后一个模式,用于捕获所有未被前面模式匹配到的表达式,起到“默认”或类似于 else 分支的作用。

2. 详细代码示例

2.1 基础菜单选择器

假设我们编写一个脚本,向用户展示一个操作菜单,并根据用户的选择执行不同的操作。

#!/bin/bash
echo "请选择一个操作:"
echo "1. 列出文件"
echo "2. 显示当前目录"
echo "3. 显示磁盘使用情况"
echo "Q. 退出"

read -p "请输入你的选择: " choice

case $choice in
    1)
        echo "正在列出当前目录下的文件:"
        ls -l
        ;;
    2)
        echo "当前目录为:"
        pwd
        ;;
    3)
        echo "磁盘使用情况信息:"
        df -h
        ;;
    Q|q) # 匹配大写 'Q' 或小写 'q'
        echo "正在退出脚本。再见!"
        exit 0
        ;;
    *) # 处理任何其他输入的默认情况
        echo "无效的选择。请输入 1、2、3 或 Q/q。"
        ;;
esac

原理解析:
在这个例子中,我们对用户输入的 $choice 变量进行了评估。

  • 如果是 1,运行 ls -l
  • 如果是 2,运行 pwd
  • 如果是 3,运行 df -h
  • 如果是 Qq,显示退出信息,并终止脚本。
  • 对于任何其他输入,将提示“无效的选择”。

2.2 根据扩展名处理不同文件类型

一项常见的系统管理任务是根据文件的扩展名执行不同的操作。case 语句非常适合处理此类需求。

#!/bin/bash
read -p "请输入一个文件名: " filename

case "$filename" in
    *.txt) # 匹配任何以 .txt 结尾的文件
        echo "正在处理文本文件:$filename"
        cat "$filename" | head -n 5 # 显示前 5 行
        ;;
    *.log) # 匹配任何以 .log 结尾的文件
        echo "正在分析日志文件:$filename"
        grep -i "error" "$filename" # 搜索包含错误的信息 (不区分大小写)
        ;;
    *.sh) # 匹配任何以 .sh 结尾的文件
        echo "正在执行 Bash 脚本:$filename"
        bash "$filename"
        ;;
    *.zip|*.tar.gz|*.tgz) # 匹配多种压缩文件扩展名
        echo "正在解压归档文件:$filename"
        tar -xf "$filename" # 示例:假设是兼容 tar 的归档文件
        ;;
    *) # 针对未识别扩展名的默认处理
        echo "此脚本无法识别或不支持 '$filename' 的文件类型。"
        echo "请考虑手动打开它。"
        ;;
esac

原理解析:
这里,脚本接收一个文件名,并在 case 模式中使用了 Shell 通配符:

  • *.txt 匹配任何以 .txt 结尾的字符串。
  • *.log 匹配任何以 .log 结尾的字符串。
  • *.sh 匹配任何以 .sh 结尾的字符串。
  • *.zip|*.tar.gz|*.tgz 匹配具有各类归档扩展名的文件。
  • * 捕获所有其他情况。
注意: 在 case "$filename" in 中,变量 $filename 被双引号包裹。这是一个好习惯,可以防止当文件名包含空格或特殊字符时出现的单词拆分或通配符展开问题。

3. 系统管理实战:服务控制脚本

在自动化系统管理任务的实战场景中,case 语句在根据用户输入管理服务或处理不同的系统事件时极为有用。

想象一个用于管理 Linux 系统上服务的脚本。管理员可能希望启动、停止、重启或检查特定服务的状态。

#!/bin/bash
SERVICE_NAME="apache2" # 示例:需要管理的服务名

echo "$SERVICE_NAME 服务管理工具"
echo "-----------------------------------"
echo "可用操作:"
echo "start   - 启动服务"
echo "stop    - 停止服务"
echo "restart - 重启服务"
echo "status  - 检查服务状态"
echo "exit    - 退出脚本"

read -p "请输入要对 $SERVICE_NAME 执行的操作: " action

case "$action" in
    start)
        echo "正在尝试启动 $SERVICE_NAME..."
        sudo systemctl start "$SERVICE_NAME"
        if [ $? -eq 0 ]; then # 检查上一条命令的退出状态码
            echo "$SERVICE_NAME 启动成功。"
        else
            echo "启动 $SERVICE_NAME 失败。请检查日志。"
        fi
        ;;
    stop)
        echo "正在尝试停止 $SERVICE_NAME..."
        sudo systemctl stop "$SERVICE_NAME"
        if [ $? -eq 0 ]; then
            echo "$SERVICE_NAME 停止成功。"
        else
            echo "停止 $SERVICE_NAME 失败。请检查日志。"
        fi
        ;;
    restart)
        echo "正在尝试重启 $SERVICE_NAME..."
        sudo systemctl restart "$SERVICE_NAME"
        if [ $? -eq 0 ]; then
            echo "$SERVICE_NAME 重启成功。"
        else
            echo "重启 $SERVICE_NAME 失败。请检查日志。"
        fi
        ;;
    status)
        echo "正在检查 $SERVICE_NAME 的状态:"
        systemctl status "$SERVICE_NAME"
        ;;
    exit)
        echo "正在退出服务管理脚本。"
        exit 0
        ;;
    *)
        echo "无效的操作:'$action'。"
        echo "请使用 'start'、'stop'、'restart'、'status' 或 'exit'。"
        ;;
esac

原理解析:
该脚本管理一个服务(例如 apache2)。用户提供一个操作 (action),case 语句引导脚本执行相应的 systemctl 命令。这展示了 case 如何简化需要响应有限且明确输入集的脚本。