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

发布时间:2026/6/30 11:08:18

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 的[[ ... ]]在if、while里写更稳、更顺的条件。会用它做字符串相等、Shell 通配模式匹配、正则~并在/||/!内组合条件。能对照[与[[的差异读懂现代 Bash 脚本里的判断写法。30 秒速览[[是 Bash 关键字不是外部命令语法与[不同不能随便换成test。字符串相等常用也可用未加引号的右侧可当作Shell 模式*?[...]匹配。~ regex做正则匹配右侧建议用变量存正则避免转义地狱。变量在[[里通常不必为防拆词而加引号但含空格、通配符时仍建议加引号。||!可直接写在[[内部文件测试-f-d等与 S05-02 相同。可移植脚本用[Bash 脚本#!/usr/bin/env bash优先[[。正文1.[[是什么if[[-f$CONFIG]];thensource$CONFIGfi[[与]]成对出现中间是条件表达式。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却满篇[[说明作者假设了 BashS13。3. 基本语法与空格[[$nameprod]][[-f$file-r$file]][[!-d$DIR]][[后、]]前要有条件运算符两侧通常加空格与[类似便于阅读。不要写成[[-f file]]会解析失败。4. 字符串相等与在[[里与都表示字符串相等Bash 中二者等价envstaging[[$envprod]]echoprod[[$envstaging]]echostaging# 输出 stagingPOSIX 的[里请用做字符串比较S05-01在[里是历史扩展别依赖。整数大小不要写在[[里用那是重定向符号。用-eq-lt或(( ))S05-04n10[[$n-lt20]]echook((n10))echook5. Shell 模式匹配本篇核心当或!右侧未加引号时Bash 把右侧当作Shell 模式pathname expansion 同款规则不是正则模式元字符含义*任意长度任意字符?恰好一个字符[abc]匹配括号内任一字符[!abc]或[^abc]不匹配括号内字符fileapp.log[[$file*.log]]echo日志文件# 真hostapi-v2.example.com[[$hostapi-*]]echoapi 前缀# 真[[$host!prod-*]]echo非 prod 主机名右侧加引号→ 只做字面字符串比较不做模式匹配pattern*.log[[$file$pattern]]# 假比的是字面量 *.log不是后缀 .log[[$file$pattern]]# 真右侧是模式 *.log左侧一般也会参与匹配语义变量含*、?时要小心# 若 user 未加引号且值为 a*b* 会按模式解释[[$useradmin-*]]稳妥写法左侧加引号需要模式时只让右侧承担通配[[$file*.log]][[$name[Pp]rod]]# 匹配 prod 或 Prod6. 正则匹配~emailuserexample.com[[$email~^[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Za-z]{2,}$]]echo像邮箱lineerror: timeout[[$line~^error:]]echo错误行要点项说明运算符~不是右侧扩展正则ERE不是 Shell 模式引用右侧加双引号时部分版本会按字面串比不要随便给整个正则加引号推荐正则放进变量右侧写$re或$re按你需要的字面/正则语义选re^[0-9]$val42[[$val~$re]]echo纯数字# 从用户输入读模式时用变量避免在 [[ 行里手写大量反斜杠捕获分组Bash 3.2匹配成功后可用BASH_REMATCHverv1.2.3if[[$ver~^v([0-9])\.([0-9])\.([0-9])$]];thenechomajor${BASH_REMATCH[1]}# 1echofull${BASH_REMATCH[0]}# 整段匹配fi7. 模式 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$WORKDIRfi9. 在[[内部用||![[$envprod-n$API_KEY]][[-z$f||-z$g]][[!$SKIPyes]]对比 POSIX 用-a-o或两个[# 老式[$envprod-a-n$API_KEY]# 更清晰仍用 [[$envprod][-n$API_KEY]# Bash 脚本里常合并为一个 [[[[$envprod-n$API_KEY]]!取反整个子表达式或谓词[[!-f$SKIP_FILE]]run_job10. 变量要不要加引号场景建议普通字符串比较$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_ENVprod||$DEPLOY_ENVproduction]];thenSTRICT1fi12.2 按文件名过滤forfin*.tar.gz;do[[-f$f$frelease-*.tar.gz]]||continueprocess$fdone12.3 校验参数格式if[[!$PORT~^[0-9]$]];thenechoPORT must be numeric2exit1fi12.4 与case的分工场景更合适的工具多个离散取值caseS06-02前缀/后缀通配[[ pattern ]]复杂结构校验[[ ~ regex ]]13. 大小写不敏感可选默认区分大小写。打开shopt -s nocasematch后[[的模式匹配与case不区分大小写shopt-snocasematch[[$ansy]]echoyes# Y、y 都行shopt-unocasematch~是否受 nocasematch 影响因 Bash 版本而异敏感逻辑请用显式字符类[Yy]。读脚本检查清单shebang 是bash还是sh[[仅适用于前者生态。想匹配*.log时右侧是否误加了引号变成字面量需要正则时是否用了~而不是 ^...$比数值是否误用了应-lt或(( ))路径、含空格变量是否仍加了$var复杂条件是一个[[ ... ... ]]还是多个[嵌套能否读懂短路练习判断题[[和[一样都是/usr/bin/[这条命令。[[ $f *.log ]]能判断$f是否以.log结尾。[[ $n ~ ^[0-9]$ ]]表示用正则判断$n是否为纯数字。[[ -f $a -r $a ]]表示文件存在、可读且为普通文件。在[里应优先用做字符串比较以与[[一致。参考答案错[[是 Bash 关键字。错右侧有引号比的是字面*.log。对。对。错[里用在[中不可靠。实操题 1模式匹配names(app.log app.txt access.log README)fornin${names[]};doif[[$n*.log]];thenecholog:$nfidone在本地运行写出输出再给nrelease-1.0.log单独测[[ $n release-*.log ]]。参考答案输出log: app.log log: access.logrelease-1.0.log与release-*.log匹配为真。实操题 2正则与 BASH_REMATCHtagbuild-20240519-rc1if[[$tag~^build-([0-9]{8})-(rc[0-9])$]];thenechodate${BASH_REMATCH[1]}rel${BASH_REMATCH[2]}fi写出echo行内容。参考答案date20240519 relrc1改错题#!/bin/shfile$1if[[$file*.log]];thengzip$filefiif[[$count10]];thenechomanyfiif[[$id~^[0-9]$]];thenechonumeric idfi参考#!/usr/bin/env bashfile$1if[[$file*.log]];thengzip--$filefiif((count10));thenechomanyfire^[0-9]$if[[$id~$re]];thenechonumeric idfi要点shebang 与[[一致模式右侧无引号数值用(( ))正则用变量避免给整段加引号。读脚本题说明下面两段各自为真时$name的大致形态# A[[$nameapi-*]]# B[[$name~^api-[a-z0-9]$]]参考答案AShell 模式api-开头后面任意字符如api-v1、api-。B正则整体须为api- 一串小写字母或数字如api-v2api-V2不匹配。下一篇预告S05-04《算术判断(( ))与let数值比较与自增》— 在条件里写(( n 0 ))、((i))与[[、-eq的分工。

相关新闻