chezmoi 模板函数 glob 完全指南:用 doublestar 模式在模板中枚举文件
2026/9/20 13:28:31 网站建设 项目流程

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.Matchglob支持超越标准 glob 的扩展语法。关键能力包括:

  • *:匹配当前层级内任意多个字符(不跨路径分隔符)。
  • ?:匹配单个字符。
  • **:递归匹配零个或多个目录层级,例如**/*.md可以匹配任意子目录下的 Markdown 文件。
  • 字符类[abc][a-z]等:匹配字符集合。
  • {a,b}花括号展开:匹配多个备选模式。
  • 支持/作为路径分隔符(与平台无关的写法),例如**/.*可匹配所有层级中的隐藏文件。

这种能力让glob天然适合"递归列出目标目录下的某类文件"这类需求,而不必逐层手动拼接路径。模式校验与匹配逻辑由 doublestar 提供,chezmoi 在 internal/chezmoi/patternset.go 中同样复用了doublestar.ValidatePatterndoublestar.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

测试同时验证了两个事实:

  1. glob "*.txt"精确匹配目录中的文本文件,逐行输出;
  2. globCaseInsensitive "*.TXT"以大小写不敏感的方式匹配,输出结果与*.txt一致——说明匹配阶段忽略大小写,但返回的是磁盘上的真实文件名。

因此,把glob的结果交给rangejoin时,可直接当作有序的、去重的路径列表使用。

源码剖析: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.Chdirc.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...) }

从源码结构可以看出三个值得注意的设计细节:

  1. 不跟随符号链接Glob包装了一个LstatFS(见 internal/chezmoi/glob.go),用Lstat代替Stat进行路径探测,并显式传入doublestar.WithNoFollow(),因此glob不会穿透符号链接目录递归,避免因循环链接导致无限递归或越出目标目录边界;
  2. IO 错误即失败doublestar.WithFailOnIOErrors()让任何读取错误(如权限不足)直接向上传播为错误,而不是静默跳过——在模板执行中会以 panic/recover 机制转化为错误信息;
  3. 大小写不敏感是显式开关globCaseInsensitive通过追加doublestar.WithCaseInsensitive()选项复用同一套实现,保持行为完全一致。

实战场景:在模板中动态生成文件清单

glob最常见的实战价值,是让模板内容随目标目录的实际内容动态变化。例如,在一台机器上自动生成一个"当前用户目录下所有 dotfiles"的清单文件:

{{- /* 列出 home 下所有隐藏文件与目录 */ -}} {{- range glob ".*" }} - {{ . }} {{- end }}

或者递归收集文档,生成项目索引:

{{- range glob "**/*.md" | sortAlpha }} - {{ . }} {{- end }}

由于glob返回的是普通切片,它可以无缝接入 chezmoi 模板中的任意管道函数(如joinsortAlphatoJson),组合出 JSON、Markdown、脚本等任意格式的输出。若需要忽略大小写匹配,将glob换成globCaseInsensitive即可,二者参数与返回值完全一致。

提示:glob面向的是目标目录,如果你需要读取源码目录中的文件(例如遍历.chezmoitemplates或数据文件),应优先考虑includeincludeTemplate等基于源码目录的函数,避免路径基准混淆。

快速验证

可以在任意目录中用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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询