在命令行操作中,我们经常需要批量处理多个文件——无论是批量查看日志、删除临时文件,还是复制特定类型的文档,逐个输入文件名不仅繁琐,还容易出错。此时,glob通配符(Globbing Patterns)便能发挥巨大作用:它通过简短的模式匹配规则,快速定位一组符合条件的文件或路径,将原本需要循环遍历的复杂操作简化为一行命令,大幅提升终端操作效率。
“glob”这一术语源自 Unix 早期的 /etc/glob 文件,该文件曾用于存储通配符匹配规则。后来,这一功能被直接集成到 Bash 等 Shell 中,但“glob”的名称被沿用至今。值得注意的是,glob通配符的概念早于正则表达式出现,可看作是最基础的“模式匹配”形式——它没有正则表达式那么复杂的语法,但胜在简洁直观、上手成本低,至今仍是 Shell 中文件批量操作的核心工具。
本文作为 Bash 替换机制系列的第二篇,将从基础到进阶,全面拆解 Bash 中 glob通配符的核心语法、实战用法、扩展特性及注意事项,帮助读者快速掌握这一“文件批量操作利器”,轻松应对日常命令行中的各类文件处理场景。
一、基础glob通配符
基础glob通配符是 Bash 的内置功能,无需额外启用,涵盖“单个字符匹配”“任意字符匹配”“字符集合匹配”等核心场景,是日常使用最频繁的匹配规则。
1.1 ?:匹配单个任意字符
? 通配符的核心作用是匹配文件名中某一位置的“一个”任意字符,它要求该位置必须存在字符(无法匹配空字符)。
实战示例:
# 场景1:匹配当前目录下,文件名长度为2(1个任意字符+后缀)的.txt文件
# 假设当前目录存在:a.txt、b.txt、ab.txt、123.txt
ls ?.txt
# 输出结果:a.txt b.txt(仅匹配1个任意字符+(.txt)的文件)
# 场景2:匹配文件名长度为3(2个任意字符+后缀)的.txt文件
ls ??.txt
# 输出结果:ab.txt(匹配2个任意字符+(.txt)的文件)
# 场景3:匹配特定前缀后的单个字符
# 假设存在:file1.txt、file2.txt、file10.txt
ls file?.txt
# 输出结果:file1.txt file2.txt(仅匹配file后接1个字符的文件,file10.txt不匹配)
关键注意:? 仅能匹配“一个”字符,若需要匹配多个字符,需连续使用多个 ?(如 ??? 匹配3个任意字符)。
1.2 *:匹配任意数量的任意字符
* 是最常用的 glob通配符,核心作用是匹配“零个或多个”任意字符(包括空字符),适合批量匹配某类特征的文件(如特定后缀、特定前缀)。
实战示例:
# 场景1:匹配当前目录下所有.txt文件
# 假设存在:a.txt、b.txt、ab.txt、test123.txt、readme.md
ls *.txt
# 输出结果:a.txt b.txt ab.txt test123.txt(所有以.txt结尾的文件)
# 场景2:匹配以特定前缀开头的文件
ls file*
# 假设存在:file、file1.txt、file2.log、file_test.csv
# 输出结果:file file1.txt file2.log file_test.csv(所有以file开头的文件,包括无后缀的file)
# 场景3:匹配任意文件(包括隐藏文件外的所有文件)
ls *
# 输出当前目录下所有非隐藏文件(隐藏文件以.开头,默认不匹配)
# 场景4:匹配多层路径中的文件(单层通配)
ls docs/*.md
# 输出docs目录下所有.md文件(仅匹配docs目录下的第一层文件,不包含子目录)
关键注意:* 默认不匹配路径分隔符(/),因此无法直接匹配子目录中的文件;同时默认不匹配隐藏文件(以 . 开头的文件)。
1.3 [...]:匹配字符集合中的任意一个字符
[...] 通配符的核心作用是匹配方括号内指定的任意一个字符,支持直接列举字符(如 [abc])或指定字符范围(如 [a-z]),适合精准匹配特定字符位置的文件。
实战示例:
# 场景1:匹配指定字符开头的文件
# 假设存在:a.txt、b.txt、c.txt、d.txt
ls [ab].txt
# 输出结果:a.txt b.txt(仅匹配a或b开头的.txt文件)
# 场景2:匹配字符范围内的文件
ls [a-c].txt
# 输出结果:a.txt b.txt c.txt(匹配a到c范围内任意字符开头的.txt文件)
# 场景3:匹配数字开头的文件
# 假设存在:1.log、2.log、3.log、a.log
ls [0-9].log
# 输出结果:1.log 2.log 3.log(匹配0到9任意数字开头的.log文件)
# 场景4:匹配混合字符与范围
ls file_[a-z0-9].txt
# 假设存在:file_a.txt、file_5.txt、file_z.txt、file_#.txt
# 输出结果:file_a.txt file_5.txt file_z.txt(匹配file_后接a-z或0-9任意字符的.txt文件)
补充说明:Bash 还支持 POSIX 字符类(如 [[:alpha:]] 匹配任意字母、[[:digit:]] 匹配任意数字、[[:space:]] 匹配空白字符),适合跨语言、跨编码的字符匹配,例如 ls [[:digit:]].txt 与 ls [0-9].txt 功能一致。
1.4 [^...] 与 [!...]:匹配不在字符集合中的任意字符
[^...] 和 [!...] 是 [...] 的否定形式,核心作用是匹配“不在”方括号内的任意一个字符(无法匹配空字符),适合排除特定字符的文件匹配场景。两者功能完全等价,可根据习惯选择使用。
实战示例:
# 场景1:排除特定字符开头的文件
# 假设存在:a.txt、b.txt、c.txt、d.txt
ls [^ab].txt
# 输出结果:c.txt d.txt(不匹配a或b开头的.txt文件)
# 场景2:排除字符范围内的文件
ls [!1-3].log
# 假设存在:1.log、2.log、3.log、4.log、5.log
# 输出结果:4.log 5.log(不匹配1到3范围内数字开头的.log文件)
# 场景3:结合前缀使用排除匹配
ls file_[!x-z].txt
# 假设存在:file_x.txt、file_y.txt、file_a.txt、file_1.txt
# 输出结果:file_a.txt file_1.txt(排除file_后接x-z字符的.txt文件)
二、进阶glob通配符
基础glob通配符适合简单场景,而进阶通配符(如大括号模式、递归匹配)能应对更复杂的需求——例如多模式批量匹配、跨目录递归匹配等。部分进阶功能需要启用 Bash 扩展选项(如 globstar)。
2.1 {...}:多模式批量匹配
{...} 通配符(大括号模式)的核心作用是匹配大括号内用逗号分隔的所有模式,支持多前缀、多后缀、多路径的批量匹配,且即使没有匹配到文件,模式也会原样展开(区别于 [...])。
实战示例:
# 场景1:匹配多个后缀的文件
ls *.{txt,log,md}
# 假设存在:a.txt、b.log、c.md、d.csv
# 输出结果:a.txt b.log c.md(匹配.txt、.log、.md三种后缀的文件)
# 场景2:匹配多个前缀的文件
ls {file,doc,report}*
# 假设存在:file1.txt、doc2.log、report3.md、test4.txt
# 输出结果:file1.txt doc2.log report3.md(匹配file、doc、report开头的文件)
# 场景3:多路径批量操作
cp {src,docs,tests}/*.txt backup/
# 将src、docs、tests三个目录下的所有.txt文件复制到backup目录
# 场景4:大括号嵌套使用(复杂模式)
ls {img_,pic_}{1..3}.{jpg,png}
# 展开为:img_1.jpg、img_1.png、img_2.jpg、img_2.png、img_3.jpg、img_3.png、pic_1.jpg...pic_3.png
# 若存在对应文件,则列出匹配结果
# 场景5:无匹配时的表现(区别于[...])
ls {x,y}.txt
# 若当前目录无x.txt和y.txt,输出:ls: cannot access 'x.txt': No such file or directory(模式展开为两个文件,分别报错)
ls [xy].txt
# 若当前目录无对应文件,输出:ls: cannot access '[xy].txt': No such file or directory(模式未展开,直接作为字符串匹配)
关键优势:{...} 支持嵌套,能快速构造复杂的多模式匹配规则,无需编写循环即可实现批量操作,效率远高于基础通配符。
2.2 {start..end}:连续范围匹配
{start..end} 是大括号模式的扩展,核心作用是匹配从 start 到 end 的连续字符或数字范围,支持数字、字母的连续范围,适合批量匹配有规律的文件(如按日期、按序号命名的文件)。
实战示例:
# 场景1:匹配连续数字范围的文件
ls report{1..5}.txt
# 展开为:report1.txt、report2.txt、report3.txt、report4.txt、report5.txt
# 若存在对应文件,则列出匹配结果
# 场景2:匹配连续字母范围的文件
ls {a..d}.log
# 展开为:a.log、b.log、c.log、d.log
# 场景3:匹配带前缀的连续数字(如月份、日期)
ls access.log.2025-{01..12}
# 匹配2025年1月到12月的access.log日志文件(access.log.2025-01 ~ access.log.2025-12)
# 场景4:组合多范围匹配
ls {2025..2026}-{01..02}-{01..03}.log
# 匹配2025-2026年、1-2月、1-3日的日志文件(如2025-01-01.log、2025-01-02.log...)
补充说明:{start..end} 支持逆序范围(如 {5..1} 展开为 5 4 3 2 1),也支持指定步长(Bash 4.0+,如 {1..10..2} 展开为 1 3 5 7 9,步长为2)。
2.3 **:递归匹配子目录(需启用 globstar)
基础的 * 无法匹配路径分隔符(/),因此无法跨目录匹配。而 ** 是 Bash 4.0+ 提供的递归通配符,需通过 shopt -s globstar 启用,核心作用是递归匹配当前目录及所有子目录中的文件/目录。
实战示例:
# 步骤1:启用globstar选项(临时启用,重启终端失效;永久启用需添加到~/.bashrc)
shopt -s globstar
# 场景1:递归匹配所有子目录中的.txt文件
ls **/*.txt
# 输出当前目录及所有子目录下的所有.txt文件(包括多层子目录,如docs/sub1/a.txt、docs/sub2/b.txt)
# 场景2:递归匹配特定目录下的文件
ls src/**/*.sh
# 输出src目录及其实所有子目录下的.sh文件
# 场景3:递归匹配所有子目录(仅目录)
ls -d **/
# 列出当前目录下所有子目录(包括多层子目录),-d选项表示仅列出目录
# 场景4:递归匹配特定类型的目录并操作
rm -rf **/__pycache__/
# 删除当前目录及所有子目录下的__pycache__目录(Python编译缓存目录)
关键注意:启用globstar 后,** 会匹配路径中的所有目录层级,包括 .(当前目录)和 ..(上级目录);若需跳过这两个目录,可搭配 shopt -s globskipdots 选项(Bash 5.2+)。
三、扩展glob通配符
对于更复杂的匹配场景(如“匹配零次或多次某模式”“否定某类文件”),基础和进阶 glob通配符难以满足需求。Bash 提供了 extglob 扩展选项(需通过 shopt -s extglob 启用),解锁“类正则”的高级匹配能力,大幅提升 glob 的灵活性。
3.1 启用 extglob 选项
# 临时启用(当前终端有效)
shopt -s extglob
# 永久启用(所有终端有效,需重启终端)
echo "shopt -s extglob" >> ~/.bashrc
source ~/.bashrc
3.2 核心扩展语法与用法
extglob 提供 5 类核心扩展语法,覆盖“重复匹配”“条件匹配”“否定匹配”等场景,语法简洁且贴近正则表达式:
| 语法格式 | 核心功能 | 实战示例 |
|---|---|---|
?(pattern) |
匹配零次或一次 pattern | ls file?(1).txt → 匹配 file.txt、file1.txt |
*(pattern) |
匹配零次或多次 pattern | ls file*(a).txt → 匹配 file.txt、filea.txt、fileaa.txt |
+(pattern) |
匹配一次或多次 pattern | ls file+(a).txt → 匹配 filea.txt、fileaa.txt(不匹配 file.txt) |
| `@(pattern1 | pattern2)` | 匹配 pattern1 或 pattern2 中的任意一个 |
!(pattern) |
匹配“不满足” pattern 的所有文件 | ls !(*.txt) → 匹配所有非 .txt 文件 |
扩展实战示例:
# 场景1:匹配含任意版本号的日志文件(如 v1、v2.1、v3.0.1)
shopt -s extglob
ls app_v+([0-9.]).log
# 展开为:匹配 app_v 后接一次或多次数字/点的.log文件(如 app_v1.log、app_v2.1.log、app_v3.0.1.log)
# 场景2:排除多个类型的文件(保留核心源码文件)
rm -rf !(src|docs|*.py|*.js|*.md)
# 删除当前目录下所有文件/目录,排除 src 目录、docs 目录,以及 .py、.js、.md 文件
# 场景3:结合递归匹配复杂目录结构
shopt -s extglob globstar
ls **/@(*test*|*spec*).@(py|js)
# 递归匹配所有子目录中,文件名含 test 或 spec、后缀为 .py 或 .js 的文件(如 test1.py、user_spec.js)
# 场景4:匹配至少包含一个数字的文件名
ls +([0-9])*.txt
# 匹配文件名中至少有一个数字的.txt文件(如 file1.txt、123test.txt)
四、glob通配符的注意事项(避坑指南)
glob通配符虽好用,但在使用过程中容易因“匹配规则误解”“特殊场景未考虑”导致错误(如误删文件、匹配不到目标)。以下是核心注意事项:
4.1 通配符的解析顺序:先解析后执行
Bash 会在执行命令前,先对命令中的 glob通配符进行“模式展开”,将通配符替换为匹配的文件列表,再执行命令。例如:
ls *.txt
# 执行流程:1. Bash 解析 *.txt → 替换为当前目录下所有.txt文件列表(如 a.txt b.txt);2. 执行 ls a.txt b.txt
避坑点:若通配符未匹配到任何文件,默认会将通配符原样作为字符串传递给命令(可能导致错误),建议启用 nullglob 选项(未匹配时返回空字符串)或 failglob 选项(未匹配时直接报错):
# 启用 nullglob:未匹配时返回空(避免误操作)
shopt -s nullglob
rm *.bak # 若无.bak文件,命令变为 rm(无参数),不会误删文件
# 启用 failglob:未匹配时直接报错(脚本更健壮)
shopt -s failglob
ls *.csv # 若无.csv文件,直接输出错误:ls: cannot access '*.csv': No such file or directory
4.2 空格文件名的处理:必须用双引号包裹
若文件名中包含空格(如“my file.txt”),直接使用 glob通配符会导致 Bash 将文件名拆分为多个参数(分词),进而执行错误命令。例如:
# 错误示例:文件名含空格
ls my *.txt # 若存在“my file.txt”,Bash 会解析为 ls my file.txt(两个参数),导致错误
# 正确示例:用双引号包裹变量/通配符结果
for f in "*.txt"; do # 错误:双引号会禁止通配符展开,直接匹配字符串“*.txt”
echo "$f"
done
# 正确写法:先展开通配符,再用双引号包裹文件变量
shopt -s nullglob
for f in *.txt; do
echo "$f" # 双引号保留空格,正确处理“my file.txt”
done
4.3 隐藏文件的匹配:默认不匹配,需特殊处理
glob通配符默认不匹配以 . 开头的隐藏文件(如 .gitignore、.bashrc)。若需匹配隐藏文件,可启用 dotglob 选项:
# 启用 dotglob:* 包含隐藏文件(除 . 和 ..)
shopt -s dotglob
ls * # 输出当前目录下所有文件(包括隐藏文件,如 .gitignore、.bashrc)
# 匹配所有隐藏文件(包括 . 和 ..)
ls .* # 不推荐:会匹配 .(当前目录)和 ..(上级目录),可能导致误操作
# 安全匹配隐藏文件(排除 . 和 ..)
shopt -s dotglob globskipdots # globskipdots 跳过 . 和 ..(Bash 5.2+)
ls * # 安全匹配所有文件(包括隐藏文件,排除 . 和 ..)
4.4 通配符不匹配路径分隔符:跨目录需特殊处理
基础 glob通配符(*、?、[...])默认不匹配路径分隔符(/),因此无法直接匹配子目录中的文件。若需跨目录匹配,需使用 **(启用 globstar)或明确指定路径层级(如 docs/*.txt)。
4.5 特殊字符的转义:需用反斜杠或单引号
若文件名中包含 glob通配符的特殊字符(如 *、?、[、]),需对这些字符进行“转义”(告诉 Bash 这是普通字符,不是通配符),转义方式为:在特殊字符前加反斜杠(\),或用单引号(')包裹文件名。例如:
# 场景:删除名为“file*.txt”的文件(文件名含 * 特殊字符)
# 错误写法:ls file*.txt → Bash 会将 * 解析为通配符,匹配所有 file 开头的.txt文件
# 正确写法1:转义特殊字符
ls file\*.txt
# 正确写法2:单引号包裹
ls 'file*.txt'
# 创建含特殊字符的文件
touch 'a?b.txt'
# 查看该文件
ls a\?b.txt # 转义 ? 字符
五、总结
glob通配符是 Bash 中“文件批量操作”的核心工具,其价值在于“用极简的语法实现高效的文件匹配”——无需编写复杂的循环或脚本,一行命令即可完成批量查看、删除、复制、移动等操作。
掌握 glob通配符的关键的是:先熟练基础语法(*、?、[...]),再逐步解锁进阶功能({...}、**),最后通过 extglob 扩展应对复杂场景。建议在日常使用中多练习、多测试(用echo 命令预览匹配结果,如 echo *.txt),避免因误匹配导致的风险。
原文 https://blog.csdn.net/2301_79518550/article/details/154698060