filepathGlob匹配分层文件名
来源:17golang原创
时间:2026-11-15 06:16:45 284浏览 收藏
Go 里用 filepath.Glob 匹配分层文件名时,最容易误判的是通配符的范围。比如 reports/*/*.json 只表示“reports 下的一层目录,再接一个 JSON 文件”,不会继续穿过第二层目录。目录层级固定时,直接把层级写进模式;层级不固定时,就用 filepath.WalkDir 遍历,再对文件名或相对路径做过滤。
官方文档:https://pkg.go.dev/path/filepath#Glob

先把分层路径翻译成固定模式
把真实目录先写成树,再把每一段替换成模式,通常比直接试写通配符更稳。假设目录如下:
reports/
├── 2026/
│ ├── jan.json
│ └── feb.json
└── archive/
└── 2025/
└── dec.json
如果只想匹配 reports/2026/jan.json 这一层,可以使用 reports/*/*.json。这里第一个 * 匹配 2026,第二个 * 匹配 jan 或 feb。* 匹配的是不含路径分隔符的字符序列,因此它不会把 archive/2025 当成一个整体。
如果目标是 reports/archive/2025/dec.json,就要把两层目录明确写出来:reports/*/*.json 不能同时覆盖这条路径;可以使用 reports/*/*/*.json,但这会把层级固定为两层目录。filepath.Glob 没有 Bash ** 那种递归通配符语义。
用 Glob 读取结果并处理两类返回
Glob 返回匹配路径切片和错误。没有文件匹配时,结果是 nil 或空切片语义,不能当成系统故障;模式语法错误时,才会得到 filepath.ErrBadPattern。另外,遍历目录时发生的普通文件系统读取错误会被 Glob 忽略,所以它适合“尽力收集固定模式文件”,不适合要求报告每一个目录读取失败的场景。
package main
import (
"errors"
"fmt"
"path/filepath"
)
func collectReports(root string) ([]string, error) {
// 模式固定为:root 下的一层目录,目录中再找 JSON 文件。
pattern := filepath.Join(root, "*", "*.json")
matches, err := filepath.Glob(pattern)
if err != nil {
// 只有模式格式错误等 ErrBadPattern 才会从 Glob 返回。
if errors.Is(err, filepath.ErrBadPattern) {
return nil, fmt.Errorf("报告匹配模式无效: %w", err)
}
return nil, err
}
if len(matches) == 0 {
// 无匹配是正常结果,由调用方决定是否降级或提示。
return []string{}, nil
}
return matches, nil
}
这个函数把“无匹配”转换成空切片,把坏模式保留为错误。生产代码还可以根据业务需要排序;不要把返回顺序当成文件名排序契约。
为什么多写一层就匹配不到
问题通常不是 Glob 失效,而是模式表达的层数和目录真实层数不同。reports/*/*.json 的路径段可以拆成:固定的 reports、一个目录通配符、一个文件名通配符。如果真实文件是 reports/archive/2025/dec.json,中间多了一个目录段,模式自然不会命中。

不要用连续星号掩盖这个差异。Go 的 filepath.Match 规则里,* 不匹配路径分隔符;Glob 使用同一套模式语法。若需求是“所有后代目录中的 JSON”,应该明确换成遍历方案。
用 WalkDir 覆盖任意深度目录
WalkDir 适合层级不固定的目录树。遍历时先跳过目录项,再只对文件名做 filepath.Match("*.json", d.Name()),这样不会把路径分隔符交给文件名模式处理。下面的函数会收集任意深度下的 JSON 文件,并把遍历错误原样返回。
package reports
import (
"fmt"
"io/fs"
"path/filepath"
)
func collectJSON(root string) ([]string, error) {
var matches []string
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
// 目录读取失败要向上返回,不能把它伪装成“没有匹配”。
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
matched, err := filepath.Match("*.json", d.Name())
if err != nil {
return fmt.Errorf("文件名模式无效: %w", err)
}
if matched {
matches = append(matches, path)
}
return nil
})
if err != nil {
return nil, err
}
return matches, nil
}
这里的取舍很明确:Glob 的代码短、边界固定、适合已知层级;WalkDir 能覆盖更深的目录,但要承担遍历成本,并自行决定忽略目录、权限错误和排序策略。
把模式选择收敛到一个收集入口
规模稍大后,最容易出现的是调用方各自拼接模式,最后有人用 Glob、有人手写递归,返回结果的错误语义也不一致。可以把固定层级和任意深度拆成两个明确入口:
package reports
import (
"path/filepath"
"sort"
)
func collect(root string, recursive bool) ([]string, error) {
if !recursive {
// 固定一层目录时使用 Glob,表达式直接反映目录契约。
return filepath.Glob(filepath.Join(root, "*", "*.json"))
}
files, err := collectJSON(root)
if err != nil {
return nil, err
}
// 遍历顺序不作为 API 契约,统一排序便于后续生成批次。
sort.Strings(files)
return files, nil
}
调用方只需要决定目录层级是否固定,不必重复理解 Glob 的通配符边界。若文件集合很大,还可以在 WalkDir 回调里做前缀、扩展名或相对深度过滤,减少后续处理量。
几个容易踩到的边界
- 只匹配文件名:
filepath.Match("*.json", d.Name())不会检查完整路径;需要完整相对路径时,应先用filepath.Rel得到相对路径,再按固定层级设计模式。 - 操作系统分隔符:文件系统路径用
filepath.Join组合;不要把 URL 的斜杠规则和本地路径规则混在一起。 - 结果为空:无匹配不是错误。只有业务明确要求至少一个文件时,才在收集函数外层把空结果转换成业务错误。
- 需要可观测失败:若权限或目录读取失败必须被记录,就优先用 WalkDir,因为 Glob 会忽略访问文件系统时的普通错误。
速查:什么时候用哪种写法
| 需求 | 推荐写法 | 原因 |
|---|---|---|
| 固定一层目录中的 JSON | filepath.Glob(root/*/*.json) | 模式短,层级契约清晰 |
| 固定两层目录中的 JSON | filepath.Glob(root/*/*/*.json) | 显式表达两层目录 |
| 任意后代目录中的 JSON | filepath.WalkDir + filepath.Match | 不依赖不存在的递归 Glob |
| 必须看到目录读取错误 | filepath.WalkDir | 回调可以接收并处理错误 |
记住一句话:filepath.Glob 解决的是“固定路径模式下有哪些匹配项”,不是“递归扫描整棵目录树”。先数清目录层级,再决定是把层级写进模式,还是把收集动作交给 WalkDir。
延伸问题
filepath.Glob 能直接用 ** 递归吗?
不能把它当作递归通配符使用。* 不跨路径分隔符;任意深度需求请使用目录遍历。
没有匹配文件时应该返回错误吗?
Glob 本身不会把无匹配当错误。是否报错取决于业务,例如“可选日报”可以返回空切片,“必须有配置”才应由业务层补充错误。
为什么 WalkDir 示例还要调用 Match?
WalkDir 负责访问目录树,Match 负责判断当前文件名是否符合规则。把职责拆开,就不会误以为 WalkDir 自带 Glob 模式。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
282 收藏
-
309 收藏
-
500 收藏
-
488 收藏
-
318 收藏
-
135 收藏
-
202 收藏
-
479 收藏
-
453 收藏
-
141 收藏
-
281 收藏
-
345 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习