麒麟 Linux||不再用一串 elif:掌握 Shell

当脚本开始变复杂,成百上千行的 if…elif…else 会让逻辑变得臃肿、难读、易错。

Shell 的 case 语句正是为这种“单变量多分支”场景而生 —— 语法简洁、模式匹配强劲、维护性高。

本文从基础语法出发,结合常见运维与自动化场景(交互菜单、服务管理、文件分类、参数解析等),逐步讲解通配符、并行匹配、Bash4 的 ;;& / ;& 等进阶用法,并通过可直接拿去运行的脚本示例,让你既能“看懂”也能“用得上”。

无论你是刚入门的脚本写作者,还是需要把脚本变得稳健可维护的运维工程师,这篇指南都会是你常驻书签的一篇实用教程。

01

什么是 case 语句?

case 语句是一种多分支选择结构,它允许你将一个变量或表达式的值与一系列模式进行匹配,并执行与第一个成功匹配的模式相关联的代码块。

它在功能上类似于 C 或 JavaScript 等语言中的 switch 语句,但语法和行为上具有 Shell 的独特之处。

使用 case 语句的主要优点在于,当处理多达三、四个或更多分支条件时,它能显著提升代码的可读性和可维护性。

1

case 语句的基本语法

case 语句的结构清晰且富有逻辑性。下面是其标准语法:

case <变量或表达式> in
  模式1)
    # 匹配模式1时执行的命令
    ;;
  模式2|模式3)
    # 匹配模式2或模式3时执行的命令
    ;;
  模式N)
    # 匹配模式N时执行的命令
    ;;
  *)
    # 所有模式都未匹配时执行的默认命令
    ;;
esac

让我们来分解这个语法的各个部分:

• case … in

语句的起始部分。

<变量或表达式> 是您希望进行判断的目标。强烈提议始终用双引号将其包裹(如 case “$var” in),以防止因变量为空或包含空格而引发的意外分词问题。

• 模式)

定义一个用于匹配的模式,后面必须紧跟一个右括号 ).

• 命令块

在模式匹配成功后需要执行的一系列命令。

• ;; (双分号)

这是 case 语句中至关重大的分隔符。它标志着一个命令块的结束,其作用类似于 C 语言中的 break,一旦执行到 ;;,整个 case 语句就会立即终止,后续的模式将不再被检测。

• | (竖线)

在模式中充当“或”操作符,允许您将多个模式组合在同一个分支中,满足其中任意一个即可。

• *)

这是一个特殊的通配符模式,可以匹配任何内容。它一般被放在 case 语句的末尾,作为“默认”分支,用于处理所有未被前面模式捕获的情况,从而增强脚本的健壮性。

• esac

case 语句的结束标记(即 case 的反写)。

2

case 语句的工作原理

case 语句的执行流程超级直接:

1)第一,Shell 会对 case 后面的 expression 进行求值。

2)然后,Shell 从上到下,依次将求得的值与每个 pattern 进行匹配。

3)一旦找到第一个匹配的 pattern,就会执行该模式对应的 commands,直到遇到 ;; 为止。

4)执行完毕后,整个 case 语句结束。

5)如果没有任何一个 pattern 能够匹配 expression 的值,并且存在 *) 默认分支,那么 *) 分支的命令将被执行。

6)如果没有任何模式匹配,也没有 *) 分支,那么 case 语句将不执行任何操作并静默退出。

02

实际应用模板

理论结合实践是最好的学习方式。

下面通过几个常见的应用场景来展示 case 语句的强劲功能。

示例 1:处理用户交互式输入

case 语句超级适合用于创建交互式菜单,根据用户的输入执行不同操作。

#!/bin/bash

echo "请选择您想安装的应用: (apache, mysql, php)"
read -r app

case $app in
    "apache")
        echo "正在准备安装 Apache..."
        # 此处添加安装 Apache 的命令
        ;;
    "mysql")
        echo "正在准备安装 MySQL..."
        # 此处添加安装 MySQL 的命令
        ;;
    "php")
        echo "正在准备安装 PHP..."
        # 此处添加安装 PHP 的命令
        ;;
    *)
        echo "错误:无效的选择 '$app'。"
        ;;
esac

在这个例子中,脚本读取用户输入,并使用 case 语句判断用户的选择,然后执行相应的操作或给出错误提示。

示例 2:系统服务管理脚本

在 Linux 系统中,/etc/init.d 目录下的许多启动脚本都广泛使用 case 语句来处理 start、stop、restart 等参数。

#!/bin/bash

# 用法: ./service.sh [start|stop|restart]

case"$1" in
    start)
        echo "正在启动服务..."
        # 启动服务的命令
        ;;
    stop)
        echo "正在停止服务..."
        # 停止服务的命令
        ;;
    restart)
        echo "正在重启服务..."
        # 重启服务的命令
        ;;
    *)
        echo "用法: $0 {start|stop|restart}"
        exit 1
        ;;
esac

这里,$1 代表传递给脚本的第一个命令行参数。

case 语句判断这个参数的值,并执行相应的服务操作。

03

高级技巧与模式匹配

case 语句的真正威力体目前其灵活的模式匹配能力上。

1

使用 | 匹配多个模式

如果你希望多个不同的模式执行一样的操作,可以使用 | (管道符) 将它们连接起来,这相当于逻辑上的“或”。

#!/bin/bash

read -p "请输入一个月份的英文缩写 (例如: Jan, Feb): " month

case $month in
    Feb)
        echo "$month 有 28 或 29 天。"
        ;;
    Apr|Jun|Sep|Nov)
        echo "$month 有 30 天。"
        ;;
    Jan|Mar|May|Jul|Aug|Oct|Dec)
        echo "$month 有 31 天。"
        ;;
    *)
        echo "无效的月份: $month"
        ;;
esac

在这个例子中,多个具有一样天数的月份被归并在同一个分支中,使代码更加简洁。

2

使用通配符进行模式匹配

case 语句的真正威力在于其灵活的模式匹配,它支持标准的 Shell 通配符,让您能够编写出极其灵活的匹配规则。

* (星号)

匹配任意长度(包括零长度)的任意字符序列。

• 示例

检查文件名后缀。

filename="document.pdf"
case "$filename" in
  *.txt) echo "这是一个文本文档。";;
  *.pdf) echo "这是一个 PDF 文件。";;
  *.jpg|*.png) echo "这是一个图像文件。";;
  *) echo "未知文件类型。";;
esac
  • ? (问号)

匹配任意单个字符。

• 示例

匹配固定长度的字符串。

code="A101"
case "$code" in
  A???) echo "这是一个A开头的4位代码。";;
  B???) echo "这是一个B开头的4位代码。";;
  *) echo "不符合规范的代码。";;
esac

[…] (字符集)

匹配方括号中指定的任意一个字符。

可以使用连字符 – 来表明一个范围。

• 示例

判断输入的首字母类型。

read -p "输入一个单词: " word
case "$word" in
  [aeiouAEIOU]*) echo "单词以元音字母开头。";;
  [a-zA-Z]*) echo "单词以辅音字母开头。";;
  [0-9]*) echo "输入以数字开头。";;
  *) echo "输入以特殊符号开头。";;
esac

3

结合 for 循环进行文件分类

case 语句可以与 for 循环结合,实现强劲的批处理功能,例如根据文件扩展名对文件进行分类。

#!/bin/bash

for filename in *; do
    case $filename in
        *.jpg|*.jpeg|*.png|*.gif)
            echo "$filename 是一个图片文件。"
            ;;
        *.sh)
            echo "$filename 是一个 Shell 脚本。"
            ;;
        *.txt|*.md)
            echo "$filename 是一个文本文档。"
            ;;
        *)
            echo "$filename 是一个未知类型的文件。"
            ;;
    esac
done

此脚本会遍历当前目录下的所有文件,并根据其扩展名进行分类输出。

04

case vs. if-elif-else:如何选择?

这是一个常见的问题。

虽然两者都能实现条件分支,但它们的适用场景有所不同。

麒麟 Linux||不再用一串 elif:掌握 Shell

代码对比: 当根据同一变量的值进行多路选择时,case 的优势超级明显。

# 使用 if-elif-else
if [ "$action" = "start" ]; then
  echo "Starting..."
elif [ "$action" = "stop" ]; then
  echo "Stopping..."
elif [ "$action" = "restart" ]; then
  echo "Restarting..."
else
  echo "Invalid action."
fi

# 使用 case,结构更清晰
case"$action" in
  start)   echo "Starting..."   ;;
  stop)    echo "Stopping..."   ;;
  restart) echo "Restarting..." ;;
  *)       echo "Invalid action." ;;
esac

05

黄金组合:while 循环与

case 语句

当 case 语句与 while 循环结合使用时,它们能发挥出 1+1>2 的效果,轻松创建出可以反复操作的交互式菜单。

while true 会创建一个无限循环,确保菜单在用户选择退出前一直显示;而 case 则完美地承担了处理用户每一次选择的任务。

下面,我们将通过两个详细的实验来实践这一强劲组合。

1

动手实验一:构建带有功能菜单的跳板机

这是 VACUUM 中的“核武器”,威力巨大,但副作用也同样致命。

核心作用

创建一个 Shell 脚本,该脚本显示一个包含多个服务器选项的菜单。

用户选择一个数字后,脚本能自动通过 SSH 登录到对应的服务器。

准备工作

为了获得最佳体验,提议配置好本地到目标服务器的 SSH 密钥认证,这样登录时就无需手动输入密码。

脚本代码 (jumpserver.sh)

#!/bin/bash

# 简单的跳板机脚本

# 使用一个无限循环来持续显示菜单,直到用户选择退出
whiletrue; do
  # 清屏,让菜单更清晰
  clear
  echo "========================================"
  echo "           跳板机登录菜单"
  echo "========================================"
  echo "  1. 登录 Web 服务器 (192.168.1.10)"
  echo "  2. 登录 数据库服务器 (192.168.1.20)"
  echo "  3. 登录 测试服务器 (192.168.1.30)"
  echo "----------------------------------------"
  echo "  q/Q. 退出"
  echo "========================================"

  # 读取用户输入
  read -p "请输入您的选择: " choice

  # 使用 case 语句处理用户输入
case"$choice" in
    1)
      echo "正在连接 Web 服务器..."
      ssh user@192.168.1.10
      read -p "按 [Enter] 键返回菜单..."
      ;;
    2)
      echo "正在连接 数据库服务器..."
      ssh user@192.168.1.20
      read -p "按 [Enter] 键返回菜单..."
      ;;
    3)
      echo "正在连接 测试服务器..."
      ssh user@192.168.1.30
      read -p "按 [Enter] 键返回菜单..."
      ;;
    q|Q)
      echo "正在退出,感谢使用!"
      break # 中断 while 循环,退出脚本
      ;;
    *)
      echo "无效输入,请输入正确的选项!"
      sleep 2 # 暂停2秒,让用户看到提示
      ;;
  esac
done

代码解析

  • while true; do … done

创建了一个无限循环,使得菜单在每次操作后都能重新显示。

  • read -p “…” choice

提示用户输入,并将输入内容存入 choice 变量。

  • case “$choice” in … esac

case 语句开始,判断 choice 变量的值。

  • 1)、2)、3)

分别匹配用户输入的数字,并执行相应的 ssh 命令。

登录会话结束后,使用 read 暂停脚本,等待用户按回车返回主菜单。

  • q|Q)

同时匹配小写的 q 和大写的 Q。当用户想退出时,执行 break 命令跳出 while 循环,从而结束脚本。

  • 6. *)

如果用户的输入不是以上任何一个模式,* 通配符会捕获它,并打印错误提示。

2

动手实验二:制作系统性能监控菜单

目标

创建一个多功能菜单,让用户可以方便地查看系统的 CPU、内存、磁盘 I/O 和网络状态。

准备工作

某些监控命令(如 iostat)可能不是系统默认安装的。

在基于 Debian/Ubuntu 的系统中,可以通过 sudo apt-get install sysstat 来安装;在 CentOS/RHEL 中,使用 sudo yum install sysstat。

脚本代码 (sys_monitor.sh)

#!/bin/bash

# 系统性能监控菜单脚本

# 定义一些颜色,让输出更好看
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
NC='33[0m' # No Color

# 函数:显示CPU信息
check_cpu() {
  echo -e "
${YELLOW}======= CPU 使用情况 =======${NC}"
  top -b -n 1 | head -n 5
  echo -e "${YELLOW}============================${NC}
"
}

# 函数:显示内存信息
check_mem() {
  echo -e "
${YELLOW}======= 内存使用情况 =======${NC}"
  free -h
  echo -e "${YELLOW}============================${NC}
"
}

# 函数:显示磁盘I/O信息
check_io() {
if ! command -v iostat &> /dev/null; then
    echo -e "
${RED}错误: iostat 命令未找到。请先安装 sysstat 包。${NC}
"
    return
  fi
  echo -e "
${YELLOW}======= 磁盘 I/O 性能 =======${NC}"
  iostat -dx 12
  echo -e "${YELLOW}=============================${NC}
"
}

# 函数:显示网络统计
check_net() {
  echo -e "
${YELLOW}======= 网络连接统计 =======${NC}"
  ss -s
  echo -e "${YELLOW}============================${NC}
"
}

# 主循环
whiletrue; do
  clear
  echo "========================================"
  echo -e "          ${GREEN}系统性能监控菜单${NC}"
  echo "========================================"
  echo "  1. 查看 CPU 性能"
  echo "  2. 查看 内存 使用情况"
  echo "  3. 查看 磁盘 I/O"
  echo "  4. 查看 网络 统计"
  echo "----------------------------------------"
  echo "  q. 退出"
  echo "========================================"

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

case"$choice" in
    1) check_cpu ;;
    2) check_mem ;;
    3) check_io ;;
    4) check_net ;;
    q|Q)
      echo "正在退出..."
      exit 0
      ;;
    *)
      echo -e "
${RED}无效输入,请重试。${NC}"
      sleep 1
      continue # 直接进入下一次循环
      ;;
  esac
  read -p "按 [Enter] 键返回菜单..."
done

分析

  • 函数化

为了代码的整洁和复用,我们将每个监控功能都封装在独立的函数中。

  • 命令详解

1)top -b -n 1 | head -n 5

以非交互模式运行 top,并截取包含CPU负载的核心信息。

2)free -h

以对人类友善的格式(GB, MB)显示内存使用情况。

3)iostat -dx 1 2

提供磁盘的读写速率、I/O 等待时间等关键指标,是诊断磁盘性能瓶颈的利器。

4)ss -s

快速显示当前系统的 TCP 连接总数等摘要信息,是 netstat 的现代高效替代品。

  • 健壮性

在 check_io 函数中,我们第一通过 command -v iostat 检查 iostat 命令是否存在,如果不存在则给出友善提示,避免了脚本因命令缺失而报错退出。

  • 用户体验

脚本使用了颜色来区分标题和错误信息,并通过 sleep 和 read 提供了适当的暂停,让用户有时间阅读输出。

06

高级技巧与最佳实践

除了构建交互式菜单,case 语句在其他场景下同样大放异彩。

1

高级分支控制 (Bash 4+)

;&

执行完当前命令块后,无条件地继续执行下一个分支的命令块(fall-through)。

;;&

执行完当前命令块后,继续向下测试后续的模式,如果匹配则执行对应的命令块。

# Bash 4+ 示例
val="apple"
case"$val" in
  a*)
    echo "Starts with 'a'."
    ;;&  # 继续测试下一个模式
  *e)
    echo "Ends with 'e'."
    ;;
esac
# 输出:
# Starts with 'a'.
# Ends with 'e'.

2

在函数中使用 case

将 case 逻辑封装在函数中,可以极大地提高代码的模块化和复用性。

# 示例:一个简单的日志函数
log() {
  local level="$1"
  local message="$2"
case"$level" in
    INFO) echo "[INFO] $message";;
    WARN) echo "[WARN] $message";;
    ERROR) echo "[ERROR] $message";;
    *) echo "[UNKNOWN] $message";;
  esac
}

log "INFO""User logged in."
log "ERROR""Database connection failed."

3

脚本参数解析

case 是解析命令行参数(如 -v, –help)的理想工具。

while 循环遍历所有参数,case 负责匹配和处理每一个参数。

#!/bin/bash
# 用法: ./myscript.sh -v --file /tmp/data

while [ "$1" != "" ]; do
case"$1" in
    -f | --file)
      shift # 移动到下一个参数(即文件名)
      FILE_PATH="$1"
      echo "文件路径设置为: $FILE_PATH"
      ;;
    -v | --verbose)
      VERBOSE=true
      echo "详细模式已开启"
      ;;
    -h | --help)
      echo "用法: $0 [-f FILE] [-v] [-h]"
      exit 0
      ;;
    *)
      echo "错误: 未知参数 $1"
      exit 1
      ;;
  esac
  shift # 移动到下一个参数,准备下一次循环
done

写在最后

case 语句并非花哨的语法糖,而是实战中提升可读性、减少漏洞、加速开发的利器。

掌握它,等于给你的脚本装上了“决策引擎”:从参数解析到交互菜单、从文件批处理到日志分级,case 都能让逻辑更清晰、扩展更容易。

同样地,要玩转 Oracle,离不开对 Linux 的熟悉与掌控。

毕竟,90% 的数据库性能问题,根源都在操作系统层面。

如果你还想系统补齐 Linux 的实战短板,推荐看看刘峰老师的 Linux 系列课程,循序渐进,从命令行到运维部署,一步到位。

文章来源:
https://mp.weixin.qq.com/s/AELgcG35HtVom61lQewNjw

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...