Synth Daily

Globstar 的起源与演变

双星号(**)通配符,也称为 globstar,是一种用于递归匹配子目录中文件的模式。它最早由 Zsh 在 1990 年代初引入,随后由 KornShell 在 2003 年独立实现并命名。最终,Bash 在 2009 年的 4.0 版本中采纳了这一特性,极大地推动了其普及。此后,** 演变为一个跨语言和工具的事实标准,被 Python、Ruby、JavaScript、Git 等广泛采用,简化了递归文件搜索的操作。

Globstar 的诞生:一个实用的创新

与只能在单个目录内匹配的星号(*)不同,双星号(**)是一个可以递归匹配多层子目录的通配符。例如,**/*.txt 会查找当前目录及其所有子目录下的全部 .txt 文件。

值得注意的是,这个功能并非源自最初的 Unix 设计,也没有被 POSIX 标准化。它是在不同的 Shell 和编程环境中作为扩展功能独立发展起来的。

Zsh:最早的先行者

Z shell(Zsh)是实现递归匹配的先驱。

  • 早期实现: Zsh 在 1990 年左右引入了递归匹配的概念,并在 1992 年的 2.2 版本中确立了我们今天熟悉的 **/ 语法。
  • 简化操作: 这一创新使得用户无需借助 find 等外部工具,就能用简单的命令(如 ls **/*.txt)列出所有子目录中的文件。
  • 独特性: 在当时,传统的 Bourne shell、C shell 和早期的 Korn shell 都不具备此功能。

KornShell 与 “globstar” 的命名

大约十年后,KornShell(ksh93)也引入了类似的功能,但这是一个独立的实现。

  • 独立发明: ksh93 在 2003 年左右添加了对 ** 的支持,并将其命名为 “globstar”
  • 命名来源: “globstar” 这个术语正是源于 ksh93 的这次实现。有趣的是,其作者 David Korn 当时并不知道 Zsh 早已实现了这个功能,他以为这是自己的原创。

我所知的是,当我在 2003 年添加它时,** 并不存在于其他 shell 中。 — David Korn, 2016

Bash 的普及之路

作为使用最广泛的 Shell,Bash 的采纳是 ** 走向主流的关键一步。

  • 版本支持: Bash 在 2009 年发布的 4.0 版本中正式引入了该功能,并沿用了 KornShell 的名称 globstar
  • 默认禁用: 为了避免破坏依赖旧有行为的脚本,globstar 在 Bash 中默认是禁用的。你需要通过 shopt -s globstar 命令来手动开启。
  • 巨大影响: Bash 的庞大用户基础使得 ** 模式迅速为广大开发者所熟知和使用。

从 Shell 到编程语言的演变

** 的简洁和实用性使其迅速超越了 Shell 的范畴,被各种编程语言和开发工具广泛采纳。

  • Ruby: 是较早的支持者之一,其文件匹配方法 Dir.glob 早在 2000 年代中期就已支持 ** 语法。
  • Python:Python 3.5 (2015) 中,通过为 glob 模块添加 recursive=True 参数,正式支持了 ** 模式。
  • JavaScript/Node.js: 虽然语言本身没有内置,但 Node.js 生态中的 globminimatch 等库使其成为前端构建工具(如 Webpack)和配置文件中的标准语法。
  • Java:Java 7 (2011) 的 NIO 文件 API 中引入了对 ** 的支持。
  • 构建与配置工具: 像 Apache Ant 这样的构建工具和 Git 的 .gitignore 文件很早就开始使用 ** 来匹配多层目录,使其成为开发工作流中不可或缺的一部分。

一个非官方标准的成功

尽管从未被正式标准化,** 凭借其强大的实用性,已经成为一个跨平台、跨语言的共识。它的发展历程清晰地展示了一个源自特定 Shell 的创新如何演变为一个被整个开发社区广泛接受的通用约定。

如果要颁奖的话,我会把创新奖颁给 Zsh,因为它发明了它;营销奖颁给 Ksh,因为它将其命名为“globstar”;而分发奖则颁给 Bash,因为它让每个人的终端都能用上它。