当前位置: 首页 > news >正文

Shell脚本精读 · S05-03 | `[[` 与模式匹配:Bash 条件表达式

模块:S05 条件表达式
篇号:S05-03 / 42
预计阅读:50 分钟
主线:Bash(必读三星· 读他人脚本的核心篇)


文章目录

    • 本篇目标
    • 30 秒速览
    • 正文
      • 1. `[[` 是什么
      • 2. 为何脚本里常见 `[[`
      • 3. 基本语法与空格
      • 4. 字符串相等:`=` 与 `==`
      • 5. Shell 模式匹配(本篇核心)
      • 6. 正则匹配:`=~`
      • 7. 模式 vs 正则:一张表
      • 8. 文件测试与空串(与 `[` 对照)
      • 9. 在 `[[` 内部用 `&&` `||` `!`
      • 10. 变量要不要加引号
      • 11. `[` 与 `[[` 对照速查
      • 12. 读脚本时的典型片段
        • 12.1 按环境名分支
        • 12.2 按文件名过滤
        • 12.3 校验参数格式
        • 12.4 与 `case` 的分工
      • 13. 大小写不敏感(可选)
    • 读脚本检查清单
    • 练习
      • 判断题
      • 实操题 1:模式匹配
      • 实操题 2:正则与 BASH_REMATCH
      • 改错题
      • 读脚本题
    • 下一篇预告

本篇目标

掌握 Bash 的[[ ... ]]:在ifwhile里写更稳、更顺的条件。会用它做字符串相等Shell 通配模式匹配正则=~,并在&&/||/!内组合条件。能对照[[[的差异,读懂现代 Bash 脚本里的判断写法。


30 秒速览

  • [[是 Bash 关键字,不是外部命令;语法与[不同,不能随便换成test
  • 字符串相等常用==(也可用=);未加引号的右侧可当作Shell 模式*?[...])匹配。
  • =~ regex正则匹配(右侧建议用变量存正则,避免转义地狱)。
  • 变量在[[通常不必为防拆词而加引号,但含空格、通配符时仍建议加引号
  • &&||!可直接写在[[内部;文件测试-f-d等与 S05-02 相同。
  • 可移植脚本[Bash 脚本#!/usr/bin/env bash)优先[[

正文

1.[[是什么

if[[-f"$CONFIG"]];thensource"$CONFIG"fi
  • [[]]成对出现,中间是条件表达式
  • Bash解析阶段处理[[,不是像[那样启动一个名为[的命令。
  • 成功(条件为真)→ 退出码0;假 →1(与 S04-01、S05-01 一致)。

[的第一条区别

["$a"="$b"]# [ 是命令,参数要按「单词」拆开[[$a==$b]]# [[ 由 Bash 解析,规则不同(见下文)

2. 为何脚本里常见[[

能力[/test[[
可移植(POSIX sh)❌ Bash 等
右侧Shell 模式*.log❌(只能比字面串)==/!=
正则=~
内部&&||!-a-o或嵌套多个[✅ 直接写
未加引号变量易拆词、易踩坑相对安全(仍建议引号)

读开源 Bash 脚本时,大量if [[ ... ]]是正常现象;若 shebang 是sh却满篇[[,说明作者假设了 Bash(S13)。


3. 基本语法与空格

[["$name"=="prod"]][[-f"$file"&&-r"$file"]][[!-d"$DIR"]]
  • [[后、]]要有条件;运算符两侧通常加空格(与[类似,便于阅读)。
  • 不要写成[[-f file]](会解析失败)。

4. 字符串相等:===

[[===都表示字符串相等(Bash 中二者等价):

env="staging"[["$env"="prod"]]&&echo"prod"[["$env"=="staging"]]&&echo"staging"# 输出 staging

POSIX 的[里请用=做字符串比较(S05-01);==[里是历史扩展,别依赖。

整数大小不要写在[[里用><(那是重定向符号)。用-eq-lt(( ))(S05-04):

n=10[["$n"-lt20]]&&echo"ok"((n>=10))&&echo"ok"

5. Shell 模式匹配(本篇核心)

==!=右侧未加引号时,Bash 把右侧当作Shell 模式(pathname expansion 同款规则,不是正则):

模式元字符含义
*任意长度任意字符
?恰好一个字符
[abc]匹配括号内任一字符
[!abc][^abc]不匹配括号内字符
file="app.log"[[$file==*.log]]&&echo"日志文件"# 真host="api-v2.example.com"[[$host==api-*]]&&echo"api 前缀"# 真[[$host!=prod-*]]&&echo"非 prod 主机名"

右侧加引号→ 只做字面字符串比较,不做模式匹配:

pattern='*.log'[[$file=="$pattern"]]# 假:比的是字面量 *.log,不是后缀 .log[[$file==$pattern]]# 真:右侧是模式 *.log

左侧一般也会参与匹配语义;变量含*?时要小心:

# 若 user 未加引号且值为 a*b,* 会按模式解释[[$user==admin-*]]

稳妥写法:左侧加引号,需要模式时只让右侧承担通配:

[["$file"==*.log]][["$name"==[Pp]rod]]# 匹配 prod 或 Prod

6. 正则匹配:=~

email="user@example.com"[[$email=~^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$]]&&echo"像邮箱"line="error: timeout"[[$line=~^error:]]&&echo"错误行"

要点:

说明
运算符=~(不是==
右侧扩展正则(ERE),不是 Shell 模式
引用右侧加双引号时,部分版本会按字面串比,不要随便给整个正则加引号
推荐正则放进变量,右侧写$re"$re"(按你需要的字面/正则语义选)
re='^[0-9]+$'val="42"[[$val=~$re]]&&echo"纯数字"# 从用户输入读模式时,用变量,避免在 [[ 行里手写大量反斜杠

捕获分组(Bash 3.2+):匹配成功后可用BASH_REMATCH

ver="v1.2.3"if[[$ver=~^v([0-9]+)\.([0-9]+)\.([0-9]+)$]];thenecho"major=${BASH_REMATCH[1]}"# 1echo"full=${BASH_REMATCH[0]}"# 整段匹配fi

7. 模式 vs 正则:一张表

需求写法右侧类型
后缀是.log[[ $f == *.log ]]Shell 模式
主机名像api-开头[[ $h == api-* ]]Shell 模式
纯数字[[ $n =~ ^[0-9]+$ ]]正则
邮箱粗略校验[[ $e =~ @.+ ]]正则

Shell 模式简单、快;正则表达力强。不要写[[ $x == ^[0-9]+$ ]](那是把^当普通字符去模式匹配,不是正则)。


8. 文件测试与空串(与[对照)

文件谓词与 S05-02 相同,可直接写在[[里:

[[-f"$CONFIG"&&-r"$CONFIG"]][[-d"$OUT"||-L"$OUT"]]

空串

[[-z"$OPT"]][[-n"${1:-}"]]

组合示例(读脚本高频):

if[[-f"$LOCK"&&-s"$LOCK"]];thenecho"已有非空锁文件"fiif[[!-d"$WORKDIR"]];thenmkdir-p"$WORKDIR"fi

9. 在[[内部用&&||!

[["$env"=="prod"&&-n"$API_KEY"]][[-z"$f"||-z"$g"]][[!"$SKIP"=="yes"]]

对比 POSIX 用-a-o或两个[

# 老式["$env"="prod"-a-n"$API_KEY"]# 更清晰(仍用 [)["$env"="prod"]&&[-n"$API_KEY"]# Bash 脚本里常合并为一个 [[[["$env"=="prod"&&-n"$API_KEY"]]

!取反整个子表达式或谓词:

[[!-f"$SKIP_FILE"]]&&run_job

10. 变量要不要加引号

场景建议
普通字符串比较"$var"
右侧要当模式右侧不加引号;左侧建议"$var"
正则$re变量;慎给含\的整段加引号
文件路径始终"$path"
# 未定义变量[[-n"${OPT:-}"]]# set -u 下安全[["${DEBUG:-}"=="1"]]

[[不会对未加引号的变量做 pathname 展开(不会像裸写$f在命令行里那样扫目录),但仍可能触发模式匹配(上一节)。


11.[[[对照速查

写法[[[
字符串相等[ "$a" = "$b" ][[ "$a" == "$b" ]]
模式匹配不支持(除非外部case[[ "$a" == foo* ]]
正则不支持[[ "$a" =~ ^foo ]]
与 / 或-a-o&&两个[[[ a && b ]]
存在文件[ -f "$f" ][[ -f "$f" ]]
命令形式test/[命令关键字,无test等价

不要混用语法

# 错:[[ 里抄 POSIX 的 = 有时可以,但把 [ 的 -a 带进 [[ 会乱[["$a"="$b"-a-n"$c"]]# 避免 -a,改用 &&# 错:把 [[ 当成命令去跑/usr/bin/[[-ffile]]# 不存在这种用法

12. 读脚本时的典型片段

12.1 按环境名分支
if[["$DEPLOY_ENV"=="prod"||"$DEPLOY_ENV"=="production"]];thenSTRICT=1fi
12.2 按文件名过滤
forfin*.tar.gz;do[[-f"$f"&&"$f"==release-*.tar.gz]]||continueprocess"$f"done
12.3 校验参数格式
if[[!"$PORT"=~^[0-9]+$]];thenecho"PORT must be numeric">&2exit1fi
12.4 与case的分工
场景更合适的工具
多个离散取值case(S06-02)
前缀/后缀通配[[ == pattern ]]
复杂结构校验[[ =~ regex ]]

13. 大小写不敏感(可选)

默认区分大小写。打开shopt -s nocasematch后,[[的模式匹配case不区分大小写:

shopt-snocasematch[["$ans"==y]]&&echo"yes"# Y、y 都行shopt-unocasematch

=~是否受 nocasematch 影响因 Bash 版本而异;敏感逻辑请用显式字符类[Yy]


读脚本检查清单

  • shebang 是bash还是sh[[仅适用于前者生态。
  • 想匹配*.log时,右侧是否误加了引号变成字面量?
  • 需要正则时是否用了=~,而不是== ^...$
  • 比数值是否误用了>(应-lt(( )))?
  • 路径、含空格变量是否仍加了"$var"
  • 复杂条件是一个[[ ... && ... ]]还是多个[嵌套?能否读懂短路?

练习

判断题

  1. [[[一样,都是/usr/bin/[这条命令。
  2. [[ $f == "*.log" ]]能判断$f是否以.log结尾。
  3. [[ $n =~ ^[0-9]+$ ]]表示用正则判断$n是否为纯数字。
  4. [[ -f "$a" && -r "$a" ]]表示文件存在、可读且为普通文件。
  5. [里应优先用==做字符串比较以与[[一致。
参考答案
  1. 错([[是 Bash 关键字)。
  2. 错(右侧有引号,比的是字面*.log)。
  3. 对。
  4. 对。
  5. 错([里用===[中不可靠)。

实操题 1:模式匹配

names=(app.log app.txt access.log README)fornin"${names[@]}";doif[[$n==*.log]];thenecho"log:$n"fidone

在本地运行,写出输出;再给n=release-1.0.log单独测[[ $n == release-*.log ]]

参考答案

输出:

log: app.log log: access.log

release-1.0.logrelease-*.log匹配为真。

实操题 2:正则与 BASH_REMATCH

tag="build-20240519-rc1"if[[$tag=~^build-([0-9]{8})-(rc[0-9]+)$]];thenecho"date=${BASH_REMATCH[1]}rel=${BASH_REMATCH[2]}"fi

写出echo行内容。

参考答案

date=20240519 rel=rc1

改错题

#!/bin/shfile="$1"if[[$file=="*.log"]];thengzip"$file"fiif[[$count>10]];thenecho"many"fiif[[$id=~"^[0-9]+$"]];thenecho"numeric id"fi
参考
#!/usr/bin/env bashfile="$1"if[["$file"==*.log]];thengzip--"$file"fiif((count>10));thenecho"many"fire='^[0-9]+$'if[[$id=~$re]];thenecho"numeric id"fi

要点:shebang 与[[一致;模式右侧无引号;数值用(( ));正则用变量避免给整段加引号。

读脚本题

说明下面两段各自为真时$name的大致形态:

# A[[$name==api-*]]# B[[$name=~^api-[a-z0-9]+$]]
参考答案
  • A:Shell 模式,api-开头,后面任意字符(如api-v1api-)。
  • B:正则,整体须为api-+ 一串小写字母或数字(如api-v2api-V2不匹配)。

下一篇预告

S05-04:《算术判断(( ))let:数值比较与自增》— 在条件里写(( n > 0 ))((i++)),与[[-eq的分工。

http://www.jsqmd.com/news/1095543/

相关文章:

  • 星元素甄选的“底层逻辑”:不靠信息差赚钱,靠效率赢信任
  • GEO优化与AI客流的提前布局,在什么时间点开展最合适?
  • 工业品短视频代运营/询盘不断还主动转介绍客户!靠谱工业品短视频代运营靠效果说话
  • 如何5分钟配置DS4Windows:让PS手柄在Windows上完美运行的终极指南
  • 公证需要去哪里办理?常见公证事项要准备哪些材料?
  • WarcraftHelper完整指南:魔兽争霸3终极免费辅助工具,彻底解决兼容性问题
  • 华为OD机试2025C卷-乘坐保密电梯[100分](Java_Python3_C++_C语言_JsNode_Go)实现100%通过率
  • 解锁CUDA Warp Shuffle:高效线程间数据交换的实战指南
  • 5分钟解锁QQ音乐加密音频:qmcdump无损转换终极指南
  • SpringBoot DTO参数校验:从基础注解到自定义规则的实战指南
  • WorkshopDL深度解析:如何跨平台获取Steam创意工坊模组
  • 【HCIA-AI笔记(微认证2)】1.2 DeepSeek训练过程介绍
  • MAX30102传感器实战:从寄存器配置到心率血氧数据采集
  • AXI协议——1.1. 从总线到接口:AXI协议全景解析
  • 质谱原理及生态
  • HyperWorks OptiStruct几何非线性的设置
  • utwget重构解析:如何用Rust打造下一代高效网络下载工具
  • 如何在3分钟内免费为Windows系统换上macOS风格鼠标指针:完整美化教程
  • 【SPSS】多因素方差分析:从原理到交互作用深度解析(含商业案例)
  • 2026唐山粘结剂厂家采购甄选攻略:玻化砖背胶、固沙宝优质源头厂家解析
  • 从glibc到musl libc:如何为你的项目选择最合适的C标准库
  • 如何一键搞定网易云音乐插件管理?BetterNCM Installer完全指南
  • 【Python实战】- 用Matplotlib定制坐标轴:科学计数法刻度的高级配置与美化
  • OpenCore Legacy Patcher技术架构深度解析:驱动层适配与系统兼容性突破
  • 华为OD机试2025C卷-分披萨[100分](Java_Python3_C++_C语言_JsNode_Go)实现100%通过率
  • 图嵌入实战指南:从Node2Vec到GraphSAGE的节点表示学习
  • 3分钟掌握TranslucentTB:免费让Windows任务栏焕然一新的终极方案
  • 51单片机蜂鸣器编程实战:从《花海》到自定义音乐播放器
  • 终极指南:3步解锁WorkshopDL完整功能,重塑跨平台模组体验
  • 实战ggplot2:构建带显著性标注与误差棒的多因素分组条形图