bmatcuk/doublestar
GitHub: bmatcuk/doublestar
Go 语言的路径模式匹配库,为标准库增加双星号(**)递归 glob 匹配支持。
Stars: 712 | Forks: 72
# doublestar
支持 `doublestar` (`**`) 模式的路径模式匹配和 glob 匹配。
[](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4)
[](https://github.com/bmatcuk/doublestar/releases)
[](https://github.com/bmatcuk/doublestar/actions)
[](https://codecov.io/github/bmatcuk/doublestar?branch=master)
[](https://github.com/sponsors/bmatcuk)
## 关于
#### [要升级吗?](UPGRADING.md)
**doublestar** 是一个 [golang] 的实现,用于路径模式匹配和
支持 "doublestar"(又称 globstar:`**`)模式的 glob 匹配。
doublestar 模式可以递归匹配文件和目录。例如,如果你
有以下目录结构:
```
grandparent
`-- parent
|-- child1
`-- child2
```
你可以使用以下模式来查找子项:`**/child*`、
`grandparent/**/child?`、`**/parent/*`,甚至单独使用 `**`(这将
递归返回所有文件和目录)。
Bash 的 globstar 是 doublestar 的灵感来源,因此它们的工作方式类似。
请注意,doublestar 必须作为一个独立的路径组件出现。像
`/path**` 这样的模式是无效的,它将被当作 `/path*` 处理,但是
`/path*/**` 应该能达到你想要的结果。此外,`/path/**` 将
匹配 path 目录下的所有目录和文件,但 `/path/**/` 将
只匹配目录。
v4 是一次以性能为重点的完全重写。此外,
[doublestar] 已更新为使用新的 [io/fs] 包进行文件系统
访问。因此,它仅受 [golang] v1.16+ 支持。
## 安装
**doublestar** 可以通过 `go get` 安装:
```
go get github.com/bmatcuk/doublestar/v4
```
要在你的代码中使用它,你必须导入它:
```
import "github.com/bmatcuk/doublestar/v4"
```
## 用法
### ErrBadPattern
```
doublestar.ErrBadPattern
```
由各种函数返回,以报告模式格式错误。目前,
该值等于 `path.ErrBadPattern`,但是为了可移植性,可能
不应依赖此等价性。
### 匹配
```
func Match(pattern, name string) (bool, error)
```
如果 `name` 匹配文件名 `pattern`([参见“模式”]),Match 返回 true。
`name` 和 `pattern` 以正斜杠 (`/`) 字符分隔,可以是相对路径或
绝对路径。
Match 要求 pattern 匹配整个 name,而不仅仅是子字符串。唯一
可能返回的错误是 `ErrBadPattern`,此时表示 pattern 格式错误。
注意:这旨在作为 `path.Match()` 的直接替代品,后者始终
使用 `'/'` 作为路径分隔符。如果你想支持使用不同
路径分隔符(如 Windows)的系统,你需要的是 `PathMatch()`。
或者,你可以在 pattern 和 name 上运行 `filepath.ToSlash()`,
然后使用此函数。
注意:用户_不应_指望返回的错误
`doublestar.ErrBadPattern` 等于 `path.ErrBadPattern`。
### MatchUnvalidated
```
func MatchUnvalidated(pattern, name string) bool
```
如果你不关心 pattern 是否有效(可能因为你已经运行过
`ValidatePattern`),MatchUnvalidated 可以提供小幅度的性能提升。
请注意,实际上只有一种情况可以实现这种性能提升:
当模式匹配在到达 `pattern` 结尾之前到达了 `name` 的结尾时,例如
`Match("a/b/c", "a")`。
### PathMatch
```
func PathMatch(pattern, name string) (bool, error)
```
如果 `name` 匹配文件名 `pattern`([参见“模式”]),PathMatch 返回 true。
Match 和 PathMatch 之间的区别在于 PathMatch 会
自动使用你系统的路径分隔符来拆分 `name` 和 `pattern`。
在路径分隔符为 `'\'` 的系统上,转义将被禁用。
注意:这旨在作为 `filepath.Match()` 的直接替代品。它假定
`pattern` 和 `name` 都使用系统的路径分隔符。如果你
无法确定,请在 `pattern` 和 `name` 上使用 `filepath.ToSlash()`,
然后改用 `Match()` 函数。
### PathMatchUnvalidated
```
func PathMatchUnvalidated(pattern, name string) bool
```
如果你不关心 pattern 是否有效(可能因为你已经运行过
`ValidatePattern`),PathMatchUnvalidated 可以提供小幅度的性能提升。
请注意,实际上只有一种情况可以实现这种性能提升:
当模式匹配在到达 `pattern` 结尾之前到达了 `name` 的结尾时,例如
`Match("a/b/c", "a")`。
### GlobOption
可以传递给 `Glob`、`GlobWalk` 或 `FilepathGlob` 的选项。可以将任意数量
的选项作为最后一个(或几个)参数以任意顺序传递给这些函数。
```
WithCaseInsensitive()
```
WithCaseInsensitive 是一个可以传递给 Glob、GlobWalk 或
FilepathGlob 的选项。如果传递,doublestar 会将所有字母字符视为
不区分大小写(即 pattern 中的 "a" 将匹配 "a" 或 "A")。这
对于像 Windows 这样默认路径不区分大小写的平台很有用。
```
WithFailOnIOErrors()
```
如果传递,doublestar 将在中止并返回遇到的 IO 错误。请注意,
如果 glob 模式引用了不存在的路径(例如
`nonexistent/path/*`),这_不_被视为 IO 错误:它被视为
没有匹配项的模式。
```
WithFailOnPatternNotExist()
```
如果传递,如果模式在任何元字符之前引用了不存在的路径
(例如 `nonexistent/path/*`),doublestar 将中止并返回 `doublestar.ErrPatternNotExist`。
请注意,alts(即 `{...}`)会在此检查之前展开。换句话说,如果 `a`
或 `b` 不存在,像 `{a,b}/*` 这样的模式可能会失败,但 `*/{a,b}` 永远不会失败,因为星号可以匹配
无内容。
```
WithFilesOnly()
```
如果传递,doublestar 将仅从 `Glob`、`GlobWalk` 或
`FilepathGlob` 返回“文件”。在此上下文中,“文件”是指任何不是目录
或指向目录的 symlink 的对象。
注意:如果与 WithNoFollow 选项结合使用,指向目录的 symlink _将_
包含在结果中,因为没有尝试去跟随该 symlink。
```
WithNoFollow()
```
如果传递,doublestar 在遍历文件系统时将不会跟随 symlink。
然而,由于 io/fs 对查询文件系统关于
symlink 的支持_非常_差,这里有一个注意事项:如果在任何元
字符之前的模式部分包含对 symlink 的引用,它将被跟随。例如,
像 `path/to/symlink/*` 这样的模式将被跟随,前提是它是一个有效
指向目录的 symlink。然而,在同一个示例中,像
`path/to/**` 这样的模式将不会遍历 `symlink`,`path/*/symlink/*` 也不会
注意:如果与 WithFilesOnly 选项结合使用,指向目录的 symlink _将_
包含在结果中,因为没有尝试去跟随该 symlink。
```
WithNoHidden()
```
如果传递,在使用通配符时,doublestar 将不匹配隐藏文件和目录(那些
以点开头的文件和目录)。这遵循了传统的 shell glob
行为,即开头的 `*` 或 `?` 默认不会匹配点文件。
仍然可以通过在模式中显式包含隐藏文件来匹配它们。
例如,`.*` 将匹配隐藏文件,而 `.config/**` 将匹配
.config 目录内的文件。
规则是:
- 对于 `**`:不进入隐藏目录
- 对于 `*` 或以 `?` 开头的模式:不匹配点文件或
目录
在 Windows 上,doublestar 将检查文件属性并以这种方式避开隐藏文件
和目录,而不是匹配文件名。因此,任何
带有 `*` 或 `?` 的模式都可能匹配到隐藏的文件/目录。
### Glob
```
func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error)
```
Glob 返回所有匹配 pattern 的文件的名称,如果没有
匹配的文件,则返回 nil。pattern 的语法与 `Match()` 中的相同。pattern
可以描述分层名称,例如 `usr/*/bin/ed`。
Glob 默认忽略文件系统错误,例如读取目录时的 I/O 错误。
唯一可能返回的错误是 `ErrBadPattern`,报告
pattern 格式错误。
要启用在 I/O 错误时中止,可以传递 `WithFailOnIOErrors` 选项。
注意:这旨在作为 `io/fs.Glob()` 的直接替代品。与
`io/fs.Glob()` 一样,即使这对你的操作系统(如 Windows)来说是不正确的,此函数也假定你的模式使用 `/` 作为路径
分隔符。如果你不确定
是否如此,可以在调用
`Glob()` 之前在你的模式上使用 `filepath.ToSlash()`。
与 `io/fs.Glob()` 一样,包含 `/./`、`/../` 或以 `/` 开头的模式
将不返回任何结果,也不返回错误。这似乎是一个 [有意的
决定](https://github.com/golang/go/issues/44092#issuecomment-774132549),
即使这有些违背直觉。你可以使用 [SplitPattern] 将模式拆分为
基础路径(用于初始化 `FS` 对象)和模式。
注意:用户_不应_指望返回的错误
`doublestar.ErrBadPattern` 等于 `path.ErrBadPattern`。
### GlobWalk
```
type GlobWalkFunc func(path string, d fs.DirEntry) error
func GlobWalk(fsys fs.FS, pattern string, fn GlobWalkFunc, opts ...GlobOption) error
```
GlobWalk 为每个匹配模式的文件调用回调函数 `fn`。
pattern 的语法与 Match() 中的相同,并且在限制方面(例如包含 `/./`、`/../` 或以 `/` 开头的模式),其行为与
Glob() 相同。pattern 可以描述分层名称,例如
usr/*/bin/ed。
如果你不需要匹配项的切片,GlobWalk 可能比 Glob 具有轻微的性能优势,
因为它可以避免为匹配项分配内存。
此外,GlobWalk 让你可以访问每个
匹配项的 `fs.DirEntry` 对象,并允许你通过从回调
函数返回非 nil 错误来提前退出。与
`io/fs.WalkDir` 一样,如果你的回调返回 `SkipDir`,GlobWalk
将跳过当前目录。这意味着如果当前路径_是_一个
目录,GlobWalk 将不会递归进入其中。如果当前路径不是
目录,则父目录的其余部分将被跳过。
GlobWalk 默认忽略文件系统错误,例如读取目录时的 I/O 错误。
GlobWalk 可能会返回 `ErrBadPattern`,报告 pattern
格式错误。
要启用在 I/O 错误时中止,可以传递 `WithFailOnIOErrors` 选项。
此外,如果回调函数 `fn` 返回错误,GlobWalk 将
立即退出并返回该错误。
与 Glob() 一样,即使这对你的操作系统(如 Windows)来说是不正确的,此函数也假定你的模式使用 `/` 作为路径
分隔符。如果你不确定
是否如此,可以在调用 GlobWalk() 之前在你的模式上使用 filepath.ToSlash()。
注意:用户_不应_指望返回的错误
`doublestar.ErrBadPattern` 等于 `path.ErrBadPattern`。
### FilepathGlob
```
func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err error)
```
FilepathGlob 返回所有匹配 pattern 的文件的名称,如果没有
匹配的文件,则返回 nil。pattern 的语法与 Match() 中的相同。
pattern 可以描述分层名称,例如 usr/*/bin/ed。
FilepathGlob 默认忽略文件系统错误,例如读取目录
时的 I/O 错误。唯一可能返回的错误是 `ErrBadPattern`,报告
pattern 格式错误。
要启用在 I/O 错误时中止,可以传递 `WithFailOnIOErrors` 选项。
注意:FilepathGlob 是一个为了方便而提供的函数,旨在作为不需要 io/fs 复杂性的用户的
`path/filepath.Glob()` 的直接替代品。基本上,它:
* 在模式上运行 `filepath.Clean()` 和 `ToSlash()`
* 运行 `SplitPattern()` 以获取基础路径和要 Glob 的模式
* 从基础路径创建一个 FS 对象,并在该模式上执行 `Glob()s`
* 将基础路径与 `Glob()` 的所有匹配项连接起来
返回的路径将使用系统的路径分隔符,就像
`filepath.Glob()` 一样。
注意:返回的错误 `doublestar.ErrBadPattern` 不等于
`filepath.ErrBadPattern`。
### SplitPattern
```
func SplitPattern(p string) (base, pattern string)
```
SplitPattern 是一个实用函数。给定一个模式,SplitPattern 将返回
两个字符串:第一个字符串是在任何未转义的“元”字符(即 `*?[{`)_之前_出现
的最后一个斜杠 (`/`) 之前的所有内容。第二
个字符串是该斜杠之后的所有内容。例如,给定模式:
```
../../path/to/meta*/**
^----------- split here
```
SplitPattern 返回 "../../path/to" 和 "meta*/**"。这对于
初始化 os.DirFS() 以调用 Glob() 非常有用,因为如果你的
模式包含 `/./` 或 `/../`,Glob() 将会静默失败。例如:
```
base, pattern := SplitPattern("../../path/to/meta*/**")
fsys := os.DirFS(base)
matches, err := Glob(fsys, pattern)
```
如果 SplitPattern 找不到拆分模式的地方(例如
`meta*/**`),它将返回 "." 和未更改的模式(在此
示例中为 `meta**`)。
请注意,SplitPattern 还会取消转义返回的
基础字符串中的任何元字符,以便它可以
直接传递给 os.DirFS()。
当然,你有责任决定返回的基础路径在你的应用程序上下文中是否是“安全”的。也许你可以使用 Match() 针对
已批准的基础目录列表进行验证?
### ValidatePattern
```
func ValidatePattern(s string) bool
```
验证模式。在 Match()、
PathMatch() 和 Glob() 中运行时会进行模式验证,因此通常你不需要调用此函数。然而,
在某些情况下这可能会很有用:例如,如果你的程序允许用户输入一个你将在稍后运行的模式,你可能想要
验证它。
ValidatePattern 假定你的模式使用 '/' 作为路径分隔符。
### ValidatePathPattern
```
func ValidatePathPattern(s string) bool
```
与 ValidatePattern 类似,只是使用你的操作系统路径分隔符。换句话说,如果你通常会使用 Match() 或 Glob(),请使用
ValidatePattern。如果你通常会使用 PathMatch(),请使用
ValidatePathPattern。请记住,即使你的操作系统使用其他分隔符,Glob()
也需要 '/' 分隔符。
### 模式
**doublestar** 在模式中支持以下特殊术语:
特殊术语 | 含义
------------- | -------
`*` | 匹配任何非路径分隔符的序列
`/**/` | 匹配零个或多个目录
`?` | 匹配任何单个非路径分隔符的字符
`[class]` | 将任何单个非路径分隔符的字符与一类字符进行匹配([参见“字符类”])
`{alt1,...}` | 如果任何一个逗号分隔的备选项匹配,则匹配一个字符序列
任何具有特殊含义的字符都可以用反斜杠 (`\`) 转义。
doublestar (`**`) 应该被路径分隔符包围,例如 `/**/`。
模式中间的 doublestar (`**`) 行为类似于 bash 的 globstar 选项:像
`path/to/**.txt` 这样的模式将返回与 `path/to/*.txt` 相同的结果。你
要找的模式应该是 `path/to/**/*.txt`。
#### 字符类
字符类支持以下内容:
类 | 含义
---------- | -------
`[abc123]` | 匹配集合中的任何单个字符
`[a-z0-9]` | 匹配 a-z 或 0-9 范围内的任何单个字符
`[125-79]` | 匹配集合 129 或范围 5-7 内的任何单个字符
`[^class]` | 匹配任何_不_匹配该类的单个字符
`[!class]` | 与 `^` 相同:否定该类
#### Glob 不是正则表达式
我偶尔会收到错误报告,说某些正则表达式风格的语法
不起作用,或者收到添加一些受正则表达式启发的
语法的功能请求。Glob 不是正则表达式。但是,如果 glob 不足以
满足你的过滤需求,我建议使用 `GlobWalk` 进行两阶段的
方法。类似以下的内容将帮助你入门:
```
var matches []string
err := doublestar.GlobWalk(fsys, pattern, func(p string, d fs.DirEntry) error {
if (customFilter(p, d)) {
matches = append(matches, p)
} else if (d.isDir()) {
return doublestar.SkipDir
}
return nil
})
return matches, err
```
在这个示例中,`pattern` 应该是一个 glob,用于第一遍获取你可能感兴趣的文件;`customFilter` 是一个进行第
二遍筛选的函数。这第二遍可以是任何内容,包括正则表达式。
尝试构建一个 `pattern`,以减少你需要在第二遍 `customFilter` 中考虑的文件数量。
最后一点注意事项:空备选项可用于构建一些更复杂的
glob。例如,`some{thing,}` 将同时匹配 "something" 和 "some"。
备选项也可以嵌套,例如 `some{thing{new,},}`,它将匹配
"somethingnew"、"something" 和 "some"。
## 性能
```
goos: darwin
goarch: amd64
pkg: github.com/bmatcuk/doublestar/v4
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkMatch-8 285639 3868 ns/op 0 B/op 0 allocs/op
BenchmarkGoMatch-8 286945 3726 ns/op 0 B/op 0 allocs/op
BenchmarkPathMatch-8 320511 3493 ns/op 0 B/op 0 allocs/op
BenchmarkGoPathMatch-8 304236 3434 ns/op 0 B/op 0 allocs/op
BenchmarkGlob-8 466 2501123 ns/op 190225 B/op 2849 allocs/op
BenchmarkGlobWalk-8 476 2536293 ns/op 184017 B/op 2750 allocs/op
BenchmarkGoGlob-8 463 2574836 ns/op 194249 B/op 2929 allocs/op
```
这些基准测试(在 `doublestar_test.go` 中)将 Match() 与 path.Match() 进行比较,
将 PathMath() 与 filepath.Match() 进行比较,并将 Glob() + GlobWalk() 与 io/fs.Glob() 进行比较。它们
只运行标准 go 包也能理解的模式(因此,没有
`{alts}` 或 `**`),以进行公平比较。当然,与其他模式元字符相比,alts 和 doublestar 的
性能会
较低。
alts 本质上就像运行多个模式,如果你的模式中有嵌套的 alts,其数量可能会变得
很大。这会影响
匹配(即 Match())和 glob 匹配(Glob())。
`**` 在匹配方面的性能实际上与常规的 `*` 非常相似,但在进行 glob 匹配时
可能会导致大量的读取操作,因为它需要递归
遍历你的文件系统。
## 赞助商
我在 2014 年的业余时间开始了这个项目,从那以后一直在维护它。
在那段时间里,它已经发展成为 Go 生态系统中最受欢迎的 glob 匹配
库之一。因此,如果 **doublestar** 是你
项目中的一个有用的库,请考虑[赞助]我的工作!我将不胜感激!
感谢你的赞助!
## 许可证
[MIT 许可证](LICENSE)
标签:EVTX分析, Go, Ruby工具, 开发工具库, 文件系统, 日志审计, 路径匹配, 通配符