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

Bash 中的 shopt -s globstar:递归 Glob 模式详解

引言

在 Unix-like 系统中,Bash(Bourne-Again SHell)作为最流行的命令行 shell,已成为开发者和系统管理员的必备工具。Bash 不仅仅是一个简单的命令解释器,它还提供了强大的文件路径扩展机制,即“globbing”(通配符匹配)。Globbing 允许用户使用如 *? 等模式来匹配文件和目录,而无需编写复杂的正则表达式或调用外部工具。这使得文件操作变得高效而直观。

然而,传统的 globbing 模式有一个显著的局限性:它无法递归遍历子目录。例如,使用 ls *.txt 只能匹配当前目录下的 .txt 文件,而忽略嵌套在子目录中的文件。这在处理复杂目录结构时常常令人沮丧,尤其是项目目录或日志文件夹中文件层层嵌套的情况下。幸运的是,Bash 4.0 版本引入了一个名为“globstar”的 shell 选项,通过 shopt -s globstar 启用,它让双星号 ** 成为递归匹配的利器。 这个选项将 globbing 的能力扩展到整个目录树,让用户能够轻松实现如 ls **/*.txt 这样的递归文件查找。

本文将详细介绍 globstar 的原理、启用方法、基本与高级用法、实际应用场景,以及与传统工具如 find 的比较,确保你能立即上手。

globstar 的历史与启用方法

globstar 并非 Bash 的原创功能,它受到了其他 shell 如 Zsh 和 Ksh93 的启发。这些 shell 早在 Bash 4.0(2009 年发布)之前就已支持递归 globbing。 Bash 的引入 globstar 旨在增强其与这些现代 shell 的兼容性,同时保持向后兼容性。默认情况下,globstar 是禁用的,这避免了潜在的意外行为——因为在旧版 Bash 中,** 仅被视为单个 * 的变体,仅匹配当前目录。

要启用 globstar,只需在 Bash 会话中执行以下命令:

shopt -s globstar

这里的 shopt 是 Bash 的内置命令,用于管理 shell 选项(shell options)。-s 参数表示“set”(设置),globstar 是选项名称。执行后,你可以通过 shopt globstar 检查状态,它会输出 globstar on。 如果想临时禁用,可以使用 shopt -u globstar-u 为 unset),或在脚本开头添加 shopt -u globstar 以确保兼容旧环境。

对于永久启用,你可以将 shopt -s globstar 添加到 ~/.bashrc 文件中。这样,每次启动 Bash 时都会自动加载。需要注意的是,globstar 要求 Bash 版本至少 4.0。如果你使用的是较旧的系统(如某些嵌入式设备),可能需要升级 Bash 或回退到 find 等工具。

在脚本中,检查 globstar 是否启用也很重要。可以使用条件语句:

if shopt -q globstar; thenecho "globstar 已启用"# 执行递归 glob 代码
elseecho "globstar 未启用,请使用 find 命令"# 备选方案
fi

shopt -q(quiet 模式)会静默执行,并根据选项状态设置退出码(0 表示 on,非 0 表示 off)。这种检查确保脚本在不同环境中鲁棒运行。

基本用法:从简单递归查找开始

启用 globstar 后,** 的行为发生质变。它不再是简单的通配符,而是递归匹配器:** 会匹配当前目录下的所有文件和零或更多层级的目录及子目录。 最经典的示例就是用户提到的 ls **/*.txt,它会递归查找当前目录树中所有 .txt 文件。

假设你的目录结构如下:

project/
├── README.txt
├── src/
│   ├── main.py
│   └── utils/
│       └── helper.txt
└── docs/└── guide.txt

不启用 globstar 时,ls *.txt 只输出 README.txt。但启用后,ls **/*.txt 会输出:

README.txt
src/utils/helper.txt
docs/guide.txt

这大大简化了文件搜索,无需 find . -name "*.txt" 的冗长语法。

另一个基本示例:列出所有子目录。使用 ls **/ 会递归列出所有目录路径,如 src/ utils/ docs/ 等。 注意末尾的 / 确保只匹配目录(详见高级用法)。

在 for 循环中,globstar 同样强大:

for file in **/*.log; doecho "处理日志: $file"# 例如:tail -f "$file"
done

这会遍历所有 .log 文件,进行批量处理,如日志分析或备份。 相比 for file in $(find . -name "*.log"); do ... done,globstar 版本更简洁,且避免了命令替换的潜在问题(如文件名中空格)。

globstar 还支持数组赋值:

txt_files=(**/*.txt)
echo "找到 ${#txt_files[@]} 个 txt 文件"
for file in "${txt_files[@]}"; doecho "$file"
done

${#txt_files[@]} 返回数组长度,便于统计文件数。这种方式比管道 ls **/*.txt | wc -l 更高效,因为它不创建子 shell。

高级用法:组合模式与排除技巧

globstar 的真正魅力在于与其它 glob 字符的组合,以及细粒度控制。Bash 的 glob 包括 *(匹配任意字符序列,不包括 /)、?(单个字符)、[ ](字符类)和 { }(大括号扩展)。启用 globstar 后,这些可以与 ** 无缝结合。

首先,区分 ****/

  • **:匹配文件和目录。例如,echo ** 会列出所有文件和目录路径,包括嵌套的。
  • **/:后跟 / 时,只匹配目录和子目录。例如,printf "%s\n" **/ 会输出所有目录路径,如 src/ docs/ src/utils/。 这类似于 find . -type d,但更简洁。

组合示例:查找特定子目录下的文件,如 **/{src,docs}/*.py。这会递归匹配 srcdocs 目录下的所有 .py 文件。 大括号 {src,docs} 是 Bash 的扩展功能,与 globstar 完美协作。

字符类示例:**/.[^.]* 匹配所有隐藏文件(以 . 开头,但不以 .. 开头)。[!.]* 排除以 . 开头的文件。 完整命令:ls **/.[!.]* 递归列出非隐藏隐藏文件?不,[!.]* 匹配不以 . 开头的文件。修正:要匹配隐藏文件,ls **/.[!.]* 实际匹配以 . 开头但第二个字符不是 . 的文件,如 .git 但排除 ..

排除模式是高级用法中的亮点。虽然 Bash glob 不原生支持否定,但可以通过 !(扩展 glob)结合。启用 shopt -s extglob 后,可以使用 !(pattern) 排除。 示例:ls **/*.txt !**/backup/*.txt 查找所有 .txt 但排除 backup 目录下的。

另一个高级技巧:深度限制。虽然 globstar 默认无深度限制,但结合 {1,3}(大括号范围)模拟:**/{1,3}/*.txt 但这不精确,因为 ** 已递归。实际中,对于深度控制,仍推荐 find。但对于模式如 **/*.{jpg,png},它高效匹配图像文件。

在脚本中,globstar 可用于条件匹配:

case "$1" in**/*.zip) unzip "$1" ;;**/*.tar.gz) tar -xzf "$1" ;;*) echo "不支持的文件类型" ;;
esac

这根据递归路径自动解压。

与 find 命令的比较:何时选择 globstar

globstar 常常被视为 find 的轻量替代品,但两者各有优劣。find 是外部命令,支持复杂过滤(如 -mtime 时间戳、-size 大小),并可执行动作(如 -exec)。globstar 则纯 shell 内部,速度更快,无需 fork 子进程。

比较示例:递归查找 .txt 文件。

  • globstar:wc -l **/*.txt 统计所有 txt 行数。
  • find:find . -name "*.txt" -exec wc -l {} + 类似,但更慢。

基准测试显示,在大型目录中,globstar 可快 2-5 倍,因为它避免了外部调用。 但 find 支持 -prune 排除目录,如 find . -path "./backup" -prune -o -name "*.txt" -print,而 globstar 需要 extglob 辅助。

何时用 globstar?简单模式匹配、脚本内循环。

何时用 find?复杂查询、权限检查。

最佳实践:小项目用 globstar,大型系统用 find。

实际应用场景:从日常到脚本编写

globstar 在实际工作中无处不在。以下是几个实用场景。

1. 文件备份与同步

备份所有源代码:tar -cf backup.tar **/{*.py,*.sh}。这打包当前目录下所有 Python 和 shell 脚本,递归子目录。 同步到远程:rsync -av **/*.html user@server:/web/ 高效传输网站文件。

2. 批量文件处理

清理旧日志:rm **/*.log.(old|bak) 删除以 .old.bak 结尾的日志。结合日期:但 globstar 无时间过滤,可与 find 混合:find . -name "**/*.log" -mtime +7 -delete

图像处理:假设有 ImageMagick,mogrify -resize 800x **/*.{jpg,png} 批量缩放所有图像。

3. 开发工作流

在 Git 项目中,搜索变更:git diff **/*.py 但 git 不直接支持;实际用 grep -r "pattern" **/*.py 递归 grep。 测试覆盖:pytest **/test_*.py 运行所有测试文件。

4. 系统管理

监控服务:ps aux | grep **/proc/[0-9]*/status 但 procfs 特殊;更实用:kill **/*.pid 清理进程 ID 文件。

在 Docker 或 CI/CD 中,globstar 简化 artifact 收集:如 GitLab CI 的 artifacts: paths: - "**/*.jar" 递归打包 JAR 文件。

5. 脚本示例:智能文件整理器

以下是一个完整脚本,利用 globstar 整理下载文件夹:

#!/bin/bash
shopt -s globstar  # 启用 globstar
shopt -s extglob   # 启用扩展 globdownloads="$HOME/Downloads"cd "$downloads" || exit 1# 移动图像
mv **/*.{jpg,jpeg,png,gif} Pictures/ 2>/dev/null# 移动文档
mv **/*.{pdf,docx,txt} Documents/ 2>/dev/null# 移动压缩包
mv **/*.{zip,rar,tar.gz} Archives/ 2>/dev/nullecho "整理完成!"

这个脚本会递归扫描 Downloads,分类移动文件。 你可以扩展它,添加日志或确认提示。

总结

shopt -s globstar 是 Bash 中一个低调却强大的功能,它将简单的 ** 转变为递归魔力,极大简化了文件操作。从 ls **/*.txt 的日常查找,到复杂脚本的批量处理,globstar 让 Bash 更接近现代 shell 的优雅。在编写自动化脚本时,记住这一工具,它将给你节省大量时间。

http://www.dtcms.com/a/438159.html

相关文章:

  • LED驱动芯片FP7208选型指南:参数、应用场景与设计要点(宽压2.5-24V,恒流0.2V)
  • K8s学习----StorageClass:实现存储资源的动态管理
  • JUC 并发编程之无锁模型详解:CAS 原理、原子类应用与 Unsafe 底层实现
  • 网站建设的销售渠道数据库网站 建设方案
  • Python学习之day03学习(文件和异常)
  • 线性代数 · SVD | 导数
  • 免费网页设计成品网站工程建设云
  • wordpress火车头自动分类绍兴百度推广优化排名
  • hadoop-mapreduce编程模型
  • 黄页网站推广公司百度答主招募入口官网
  • AutoOps:简化自管理 Elasticsearch 的旅程
  • python如何批量下载图片
  • PDF中表格的处理 (OCR)
  • 怎样查网站空间地址代理公司注册的价格
  • LangChain源码分析(一)- LLM大语言模型
  • Android setContentView源码与原理分析
  • dlink nas建设网站有什么免费推广项目的好软件
  • 开源 C++ QT QML 开发(一)基本介绍
  • Java学习笔记Day14
  • C++进阶(4)——C++11右值引用和移动语义
  • 从入门到精通【Redis】理解Redis主从复制
  • 公司网站不备案wordpress地址怎么打开
  • 柯西显威:一道最值题的降维打击
  • Java 集合 “Map(2)”面试清单(含超通俗生活案例与深度理解)
  • 网站怎么做悬浮图片放大带后台的网站模板下载
  • java学习:四大排序
  • npm install 中的 --save 和 --save-dev 使用说明
  • 个人网站欣赏h5网站和传统网站区别
  • Inception V3--J9
  • Spring——编程式事务