chezmoi 模板函数 glob 完全指南:用 doublestar 模式在模板中枚举文件
【免费下载链接】chezmoiManage your dotfiles across multiple diverse machines, securely.项目地址: https://gitcode.com/gh_mirrors/ch/chezmoi
导读
glob是 chezmoi 模板系统中用于枚举文件列表的模板函数,它基于 doublestar/v4 为骨架,结合 chezmoi 源码实现与 txtar 测试用例,讲解glob的语法、语义、源码原理与实战用法,帮助你用它在模板中动态生成文件清单、批量遍历目标目录内容。
函数签名与基本语义
官方参考文档给出的定义非常简洁:
glob pattern- 入参:
pattern,一个 glob 模式字符串。 - 返回值:匹配该模式的文件列表(
[]string,即模板中的字符串切片)。 - 匹配引擎:
doublestar.Glob,即 github.com/bmatcuk/doublestar/v4 提供的实现。 - 路径基准:相对路径相对于目标目录(destination directory)解析。
其中相对路径基准是 chezmoi 模板函数中一个容易忽略但至关重要的语义:glob不是相对模板文件本身所在目录,也不是相对源码目录(source directory),而是相对 chezmoi 应用配置后的目标目录(通常是 home 目录)。这一点在后面的源码剖析中会得到验证。
在模板中的基本用法
glob返回的是字符串切片,可以直接遍历,也可以配合管道函数做格式化处理。在 chezmoi 模板文件(扩展名为.tmpl的源文件)或chezmoi execute-template中都可以使用:
{{- range glob ".*" }} ...处理匹配到的每个文件/目录... {{- end }}与join等函数组合,可以快速把匹配结果拼成一行文本:
{{ glob "*.txt" | join "\n" }}关于模板函数如何注册进模板上下文,见 internal/cmd/config.go 中的模板函数注册表:
"glob": c.globTemplateFunc, "globCaseInsensitive": c.globCaseInsensitiveTemplateFunc,模式语法:支持 doublestar 递归匹配
由于底层使用 doublestar 而非 Go 标准库的filepath.Match,glob支持超越标准 glob 的扩展语法。关键能力包括:
*:匹配当前层级内任意多个字符(不跨路径分隔符)。?:匹配单个字符。**:递归匹配零个或多个目录层级,例如**/*.md可以匹配任意子目录下的 Markdown 文件。- 字符类
[abc]、[a-z]等:匹配字符集合。 {a,b}花括号展开:匹配多个备选模式。- 支持
/作为路径分隔符(与平台无关的写法),例如**/.*可匹配所有层级中的隐藏文件。
这种能力让glob天然适合"递归列出目标目录下的某类文件"这类需求,而不必逐层手动拼接路径。模式校验与匹配逻辑由 doublestar 提供,chezmoi 在 internal/chezmoi/patternset.go 中同样复用了doublestar.ValidatePattern与doublestar.Match,说明 doublestar 语义贯穿于 chezmoi 的路径模式体系。
返回结果:列表与排序
glob返回匹配项组成的切片。在 txtar 测试 internal/cmd/testdata/scripts/templatefuncs.txtar 中可以看到其行为:
# test glob template function exec chezmoi execute-template '{{ glob "*.txt" | join "\n" }}{{ "\n" }}' cmp stdout golden/glob # test globCaseInsensitive template function exec chezmoi execute-template '{{ globCaseInsensitive "*.TXT" | join "\n" }}{{ "\n" }}' cmp stdout golden/glob对应的期望输出(golden/glob)为:
file1.txt file2.txt测试同时验证了两个事实:
glob "*.txt"精确匹配目录中的文本文件,逐行输出;globCaseInsensitive "*.TXT"以大小写不敏感的方式匹配,输出结果与*.txt一致——说明匹配阶段忽略大小写,但返回的是磁盘上的真实文件名。
因此,把glob的结果交给range或join时,可直接当作有序的、去重的路径列表使用。
源码剖析:glob 在 chezmoi 内部如何执行
glob模板函数的实现位于 internal/cmd/templatefuncs.go:
func (c *Config) globCaseInsensitiveTemplateFunc(pattern string) []string { return c.globTemplateFuncHelper(pattern, doublestar.WithCaseInsensitive()) } func (c *Config) globTemplateFunc(pattern string) []string { return c.globTemplateFuncHelper(pattern) } func (c *Config) globTemplateFuncHelper(pattern string, options ...doublestar.GlobOption) []string { defer func() { // 恢复执行前的当前工作目录 ... }() must(os.Chdir(c.DestDirAbsPath.String())) return mustValue(chezmoi.Glob(c.fileSystem, filepath.ToSlash(pattern), options...)) }这段实现印证了参考文档的两个关键点:
- 目标目录语义:执行前先
os.Chdir到c.DestDirAbsPath(目标目录绝对路径),因此相对模式一定是在目标目录下解析的; - 平台无关模式:
pattern经过filepath.ToSlash统一转换为/分隔,保证同一模式在 Windows 与 Unix 上行为一致。
真正的匹配由 internal/chezmoi/glob.go 中的chezmoi.Glob完成:
func Glob(fileSystem vfs.FS, prefix string, options ...doublestar.GlobOption) ([]string, error) { fsys := LstatFS{Wrapped: fileSystem} opts := slices.Concat([]doublestar.GlobOption{ doublestar.WithFailOnIOErrors(), doublestar.WithNoFollow(), }, options) return doublestar.Glob(fsys, prefix, opts...) }从源码结构可以看出三个值得注意的设计细节:
- 不跟随符号链接:
Glob包装了一个LstatFS(见 internal/chezmoi/glob.go),用Lstat代替Stat进行路径探测,并显式传入doublestar.WithNoFollow(),因此glob不会穿透符号链接目录递归,避免因循环链接导致无限递归或越出目标目录边界; - IO 错误即失败:
doublestar.WithFailOnIOErrors()让任何读取错误(如权限不足)直接向上传播为错误,而不是静默跳过——在模板执行中会以 panic/recover 机制转化为错误信息; - 大小写不敏感是显式开关:
globCaseInsensitive通过追加doublestar.WithCaseInsensitive()选项复用同一套实现,保持行为完全一致。
实战场景:在模板中动态生成文件清单
glob最常见的实战价值,是让模板内容随目标目录的实际内容动态变化。例如,在一台机器上自动生成一个"当前用户目录下所有 dotfiles"的清单文件:
{{- /* 列出 home 下所有隐藏文件与目录 */ -}} {{- range glob ".*" }} - {{ . }} {{- end }}或者递归收集文档,生成项目索引:
{{- range glob "**/*.md" | sortAlpha }} - {{ . }} {{- end }}由于glob返回的是普通切片,它可以无缝接入 chezmoi 模板中的任意管道函数(如join、sortAlpha、toJson),组合出 JSON、Markdown、脚本等任意格式的输出。若需要忽略大小写匹配,将glob换成globCaseInsensitive即可,二者参数与返回值完全一致。
提示:
glob面向的是目标目录,如果你需要读取源码目录中的文件(例如遍历.chezmoitemplates或数据文件),应优先考虑include、includeTemplate等基于源码目录的函数,避免路径基准混淆。
快速验证
可以在任意目录中用chezmoi execute-template直接验证glob的行为(命令本身不会修改任何文件):
$ touch file1.txt file2.txt $ chezmoi execute-template '{{ glob "*.txt" | join "\n" }}' file1.txt file2.txt这与仓库内置的 templatefuncs.txtar 回归测试路径完全一致,可以作为本地调试与教学验证的可靠手段。
小结
| 要点 | 说明 |
|---|---|
| 语法 | glob pattern,返回匹配文件列表 |
| 匹配引擎 | doublestar/v4,支持**、?、字符类、花括号展开 |
| 路径基准 | 相对目标目录(destination directory)解析 |
| 符号链接 | 默认不跟随(Lstat+WithNoFollow) |
| IO 错误 | 显式失败(WithFailOnIOErrors) |
| 大小写 | glob区分大小写;globCaseInsensitive不区分 |
| 平台差异 | 模式统一按/分隔,跨平台一致 |
glob是 chezmoi 模板生态中"动态化"的重要拼图:借助 doublestar 的递归模式与目标目录语义,它可以安全、跨平台地枚举文件系统内容,为模板注入运行时数据。想深入了解其实现,可继续阅读 glob.go 与 templatefuncs.go;完整的模板函数清单与使用规范,参见 模板函数参考 所在目录的其余文档。
【免费下载链接】chezmoiManage your dotfiles across multiple diverse machines, securely.项目地址: https://gitcode.com/gh_mirrors/ch/chezmoi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考