Unix 早期版本的命令解释器依赖于一个单独的程序来将未引用参数中的通配符扩展为命令:/etc/glob。该程序执行了扩展,并将扩展后的文件路径列表提供给命令执行。其名称是“global command”的缩写。后来,这个功能被提供为库函数 glob(),由 shell 等程序使用。

glob 通配符早于正则表达式出现,没有正则表达式强大,但提供了简单方便的功用于匹配符合指定模式的文件集合。

glog 通配符现在常用于执行 命令行 例如 ls *.js 或者在一些 ignore 文件,例如 .gitignore 中,或者用到路径匹配功能的工具,例如代理工具 whistle 支持 glob 路径匹配。

glob 语法

通配符 描述 例子 匹配 不匹配
* 匹配任意数量的任何字符,包括无字符 Law* Law, Laws, Lawyer GrokLaw, La, aw
? 匹配任何 单个 字符 ?at Cat, cat, Bat, bat at
** 匹配任意数量的文件夹和子文件夹
[abc] 匹配括号中给出的一个字符 [CB]at Cat, Bat cat, bat
[a-z] 匹配括号中给出的范围中的一个字符 Letter[0-9] Letter0, Letter1 … Letter9 Letters, Letter, Letter10
[!abc][^abc] 匹配括号中未给出的一个字符 [!C]at Bat, bat, cat Cat
[!a-z] 匹配不在括号内给定范围内的一个字符 Letter[!3-5] Letter1… Letter3 … Letter5, Letterxx
{a,b} 匹配括号中给出的多个字符/通配符 {C,B}at Cat, Bat cat, bat
{start..end} 会匹配连续范围的字符 d{a..d}g dag, dbg, dcg, ddg

注意事项

  1. 通配符是先解释,再执行
    Bash 接收到命令以后,发现里面有通配符,会进行通配符扩展,然后再执行命令。
    1
    2
    $ ls a*.txt
    ab.txt
    上面命令的执行过程是,Bash 先将 a*.txt 扩展成 ab.txt ,然后再执行 ls ab.txt
  2. 通配符不匹配,会原样输出
    Bash 扩展通配符的时候,发现不存在匹配的文件,会将通配符原样输出。
    1
    2
    3
    # 不存在 r 开头的文件名
    $ echo r*
    r*
    上面代码中,由于不存在 r 开头的文件名,r* 会原样输出。
  3. 只适用于单层路径
    上面所有通配符只匹配单层路径,不能跨目录匹配,即无法匹配子目录里面的文件。或者说,?* 这样的通配符,不能匹配路径分隔符 (/)
    如果要匹配子目录里面的文件,可以写成下面这样:
    1
    $ ls */*.txt
  4. 可用于文件名
    Bash 允许文件名使用通配符。这时,引用文件名的时候,需要把文件名放在单引号里面。
    1
    2
    3
    $ touch 'fo*'
    $ ls
    fo*

gitignore

git 中的 .gitignore 文件可以使用 glob 模式匹配,另外还有一些规则:

  • 所有空行或者以 # 开头的行都会被 Git 忽略
  • 匹配模式可以以 / 开头防止递归
  • 匹配模式可以以 / 结尾指定目录
  • 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号 ! 取反

vite

Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:

1
const modules = import.meta.glob('./dir/*.js');

以上将会被转译为下面的样子:

1
2
3
4
5
// vite 生成的代码
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
'./dir/bar.js': () => import('./dir/bar.js')
};

你可以遍历 modules 对象的 key 值来访问相应的模块:

1
2
3
4
5
for (const path in modules) {
modules[path]().then(mod => {
console.log(path, mod);
});
}

匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以传入 { eager: true } 作为第二个参数:

1
const modules = import.meta.glob('./dir/*.js', { eager: true });

以上会被转译为下面的样子:

1
2
3
4
5
6
7
// vite 生成的代码
import * as __glob__0_0 from './dir/foo.js';
import * as __glob__0_1 from './dir/bar.js';
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1
};

相关 node 库

https://github.com/mrmlnc/fast-glob
https://github.com/isaacs/node-glob
https://github.com/sindresorhus/globby