linux shell 编程:case模式匹配语句

介绍

case 是 bash/sh 脚本里的多分支条件语句,用来匹配字符串,等价于其他语言的 switch。

语法格式

case 变量 in
模式1)
    命令
    ;;
模式2|模式3)
    命令
    ;;
*)
    默认命令
    ;;
esac

关键字

in、)、;;、*、esac(case反过来写,表示结束)

规则

  1. 匹配顺序从上到下,匹配成功就执行对应代码块,执行完;;直接跳出case,不会继续向下匹配。
  2. 模式支持通配符:
    • *:匹配任意任意长度任意字符(类似正则 .*)
    • ?:匹配单个任意字符
    • [abc]:匹配a/b/c其中一个字符
  3. | 用来多个模式或,模式1|模式2),满足任意一个就命中。
  4. ;; 必须写,代表分支结束;少写会继续往下执行下一个分支(穿透)。
  5. *) 放最后,所有都不匹配时执行,相当于 default,可选。

例子:匹配数字

#!/bin/bash

echo "菜单"
echo "1. 启动"
echo "2. 停止"
echo "3. 重启"
echo "4. 状态"


# 定义option变量,读取用户的输入内容,赋值给option
read -p "请输入操作编号: " option

echo "测试option的值:$option"

case $option in
    1)
        echo "模拟启动服务"
        ;;
    2)
        echo "模拟停止服务"
        ;;
    3)
        echo "模拟重启服务"
        ;;
    4)
        echo "模拟查看服务状态"
        ;;
    *)
    echo "输入无效,请重新输入!"
esac


echo "脚本执行结束"

例子:匹配字符串

很多shell脚本用case解析启动参数:

#!/bin/bash
case "$1" in
start)
    echo "启动服务"
    ;;
stop)
    echo "停止服务"
    ;;
restart)
    echo "重启服务"
    ;;
help|--help|-h)
    echo "帮助文档"
    ;;
*)
    echo "用法: $0 start|stop|restart|help"
    exit 1
    ;;
esac

提示:"$1" 加双引号,防止变量为空、含空格导致语法报错。

通配符

重点:case 使用的是 Shell Glob(文件名通配符),不是正则表达式!

只支持 glob,不识别 ^ $ . + () 这类正则元字符。

支持的 3 个基础 glob 符号

符号 含义 说明
* 匹配任意长度任意字符(包括空字符串) 匹配0个或多个字符
? 匹配单个任意字符 必须有1个字符,不能为空
[字符集] 匹配括号内任意单个字符 只匹配1个字符

语法模板:

case "$var" in
  模式) 命令 ;;
esac

逐个详解 + case 示例

1. * 任意字符(0或多个)

  • *.log:以 .log 结尾的字符串
  • test*:以 test 开头
  • *error*:包含 error
  • *:匹配所有内容,一般放在最后做默认分支
file="app.log"
case "$file" in
*.log)
  echo "日志文件"
  ;;
test*)
  echo "以test开头"
  ;;
*error*)
  echo "包含error关键字"
  ;;
*)
  echo "其他文件"
  ;;
esac

2. ? 单个任意字符(必须存在1个字符)

? 代表恰好 1 位字符,不能为空

  • file?:file后面跟1个字符,file1、fileA 匹配;file、file12 不匹配
  • ???.txt:3个字符 + .txt,如 abc.txt
name="app1"
case "$name" in
app?)
  echo "app后面跟1个字符"
  ;;
esac

app → 不匹配;app1 ✔;app11 ❌

3. [] 字符集合,匹配单个字符

格式 [abc]、[0-9]、[a-z]、[A-Z]

  • [0-9]:单个数字 0~9
  • [a-z]:小写英文字母
  • [A-Z]:大写英文字母
  • [xy]:只能是 x 或者 y
  • [!0-9]:取反,匹配任意一个不是数字的字符(部分shell也可用 [^0-9],推荐 [!] POSIX标准)
read -p "输入单个字符:" c
case "$c" in
[0-9])
  echo "是数字"
  ;;
[a-z])
  echo "小写字母"
  ;;
[A-Z])
  echo "大写字母"
  ;;
[!0-9a-zA-Z])
  echo "特殊符号"
  ;;
esac

⚠️ 注意:[] 只匹配1个字符,不能匹配一串。
[0-9][0-9] 才是两个数字。


组合使用示例

str="a5"
case "$str" in
[a-z][0-9])
  echo "1个小写字母 + 1个数字"
  ;;
esac

a5 ✔;aa ❌;a55 ❌

多模式匹配

模式1|模式2,满足任意一个就匹配

case "$opt" in
start|restart)
  echo "启动或重启"
  ;;
*.jpg|*.png|*.gif)
  echo "图片文件"
  ;;
esac

误区

❌ 误区1:case里面写正则

case 不支持正则!

  • 不能用 ^ 开头锚定、$结尾锚定、.代表任意字符、+多次匹配
    # 错误写法!case不识别 ^
    case "$var" in
    ^test) echo 匹配 ;;
    esac
    

    想要正则匹配:改用 if [[ $var =~ 正则 ]]

❌ 误区2:* 匹配换行

case 是字符串比较,变量默认按单行处理,一般不匹配换行。

❌ 误区3:[0-9] 匹配多个数字

[0-9] 只匹配一个数字。
匹配1~多位数字:case原生glob做不到,要用:

if [[ $num =~ ^[0-9]+$ ]];then
  echo 纯数字
fi
❌ 误区4:变量不加双引号 "$var"

如果变量值包含空格、特殊字符,不加引号会分词破坏匹配。case里变量建议永远双引号包裹。

glob 转义

如果想把 * ? [ 当成普通字符,需要转义:
\*、\?、\[

s="a*b"
case "$s" in
a\*b)
  echo "字符串是a*b"
  ;;
esac

case 和 if 的区别

  • case:字符串模式匹配,适合多值枚举、文件名、参数判断,代码清爽;只能做等值/通配匹配,不能直接比较大小(不能写 $a -gt 10)
  • if:适合逻辑运算、数值比较、复杂条件,&& || 组合更灵活

如果要在case里做数值大小判断,可以配合 (( )) 或命令返回值:

read n
case 1 in
$(( n > 10 )) )
    echo "大于10"
    ;;
esac

常见坑

  1. 漏写 ;; → 分支穿透,多个分支一起执行
  2. 模式后少写右括号 ) → 语法报错
  3. 变量不加引号,变量带空格时解析异常
  4. 把正则写进case模式:case不是正则!是shell glob通配符,不能直接用 .*、^、$,这点很容易踩坑。

    如果要用正则匹配,改用 if [[ $var =~ 正则 ]]