path.Match不支持递归匹配,因它仅做单层字符串匹配且不能跨/;filepath.Glob不支持*但可遍历文件系统;真正递归需用filepath.WalkDir结合path.Match并统一路径分隔符。

Go 标准库不支持 ** 递归 glob,path.Match 只做单层字符串比对,真要“灵活匹配文件路径”,得组合 filepath.Glob 或 filepath.WalkDir + path.Match,且必须处理路径分隔符和平台差异。
为什么 path.Match 匹配不了带路径的文件名?
path.Match 不扫描磁盘,它只拿你给的完整字符串(比如 "./src/main.go")和 pattern(比如 "*.go")做纯字符串匹配。而 * 在它眼里不能跨 / —— 所以 path.Match("*.go", "./src/main.go") 必然返回 false。
-
path.Match("*.go", "main.go")→true(无路径前缀) -
path.Match("src/*.go", "src/main.go")→true(显式写出一级目录) -
path.Match("**/*.go", "src/main.go")→false, nil(**是非法 pattern,标准库根本不认)
它适合校验 HTTP 路由路径、配置项白名单这类“已知完整路径”的场景,不是用来找文件的。
filepath.Glob 能做什么、不能做什么?
filepath.Glob 才是真正触发文件系统遍历的函数,但它依然不支持 **。它的 pattern 行为接近 shell,但有关键限制:
立即学习“go语言免费学习笔记(深入)”;
-
*不跨路径分隔符:"foo/*.txt"不会匹配"foo/bar/baz.txt" - 不展开
~、不支持{a,b}花括号、不自动转义空格 - Windows 下传
"C:\logs\*.log"很可能失败 —— 应统一用/写法:"C:/logs/*.log" - 返回路径格式与 pattern 一致:
"*.go"返回相对路径,"/tmp/*.log"返回绝对路径
示例:在变量目录中搜索 .go 文件
srcDir := "/home/user/project" pattern := filepath.Join(srcDir, "**", "*.go") // ❌ 无效,** 不被识别 pattern = filepath.Join(srcDir, "*.go") // ✅ 只搜 srcDir 本层 files, _ := filepath.Glob(pattern)
真正灵活的递归匹配:用 filepath.WalkDir + path.Match
当你要实现 **/*.go 或按多级规则过滤(比如只匹配 cmd/ 和 internal/ 下的文件),filepath.WalkDir 是可控、无依赖、跨平台的解法:
- 先用
filepath.WalkDir遍历所有路径,每拿到一个entry就调path.Match判断是否符合你的 pattern - pattern 用 Unix 风格写(如
"*/main.go"、"cmd/**.go"),并在匹配前统一路径分隔符:path.Match(pattern, filepath.ToSlash(entry.Name())) - 注意:传给
path.Match的 name 应是相对路径片段(如"src/util.go"),不是完整绝对路径 - 性能敏感时,在循环外预编译 pattern 或缓存
filepath.ToSlash结果
容易漏掉的关键点:Windows 盘符路径(C:\)必须先转成 C:/ 再参与匹配,否则 path.Match 会把 \ 当普通字符处理。
高频踩坑总结
别写 filepath.Match —— 它根本不存在;别信文档里说的 “** 支持” —— 标准库至今(1.22)没加;别在循环里反复调 filepath.Glob 查同一 pattern —— 每次都重新遍历磁盘。
最常被忽略的是路径分隔符归一化:无论输入来自用户、配置还是 os.Args,进 path.Match 前必须过一遍 filepath.ToSlash,进 filepath.Glob 前最好也用 filepath.FromSlash 或直接确保 pattern 用 /。


















