如何在Go语言中使用双星号通配符?

19

看起来Go是为数不多的几种语言之一,似乎不理解双星号(“globstar”)文件匹配语法。至少这个语法不像预期的那样工作:

filepath.Glob(dir + "/**/*.bundle/*.txt")

关于 filepath 的实现,我是否漏掉了什么?有没有支持它的库?


2
你在哪里看到 filepath.Glob 应该支持 ** - JimB
2
文档描述不够具体。“模式可以描述层次结构名称,例如 /usr/*/bin/ed”,我所知道的大多数通配符实现都支持它。 - tcurdt
2
虽然不常见,但 ** 并不是一个标准的东西。文档非常明确:'*' 匹配任何非分隔符字符序列。http://golang.org/pkg/path/filepath/#Match - JimB
1
http://golang.org/pkg/path/filepath/#Glob 的文档并不完整。深入了解后,我发现 Match 被使用了,但这对于文档来说应该是无关紧要的。至少应该提供一个参考。 - tcurdt
我认为文档不应该规定它所不做的一切。虽然没有提到其他 shell globbing 模式(比 ** 更常见,存在更长时间),但我也不会认为它支持这些模式。 - JimB
我认为它也不应该指定它不做什么。问题在于“模式可以描述层次结构名称,例如”的不精确性和缺乏对Match的参考。我认为提供规范文档链接并不过分要求。 - tcurdt
3个回答

12

filepath.Glob 的实现在内部使用 filepath.Match。结果发现该规范并不涵盖相当常见的双星号模式(.gitignorezsh)。虽然不完全相同,但对于我的用例,我成功地通过这个小函数解决了它:

func glob(dir string, ext string) ([]string, error) {

  files := []string{}
  err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
    if filepath.Ext(path) == ext {
      files = append(files, path)
    }
    return nil
  })

  return files, err
}

我仍然期待有更好的实现方式,能够正确地进行双星匹配。


我知道[]string是字符串的切片,但是[]string{}是什么意思?为什么要在末尾添加{}?谢谢。 - Leahcim
6
[]string{}是一个切片字面量,功能上等同于make([]string, 0) - JimB
我创建了这个程序,它可以满足我的需求 - 它允许你执行 matches, err := filepathx.Globs(strings.Split(glob, "**")).Expand()。https://github.com/yargevad/filepathx - Dave Gray

6

我编写了一个小型的扩展库,支持双星号全局匹配。有了这个目录结构:

$ find a
a
a/b
a/b/c.d
a/b/c.d/e.f

您可以使用a/**/*.*来匹配包含点的在a目录下的所有内容,如下所示:
package main

import (
  "fmt"

  "github.com/yargevad/filepathx"
)

func main() {
  matches, err := filepathx.Glob("./a/**/*.*")
  if err != nil {
    panic(err)
  }

  for _, match := range matches {
    fmt.Printf("MATCH: [%v]\n", match)
  }
}

这将输出:

$ go run example.go
MATCH: [a/b/c.d]
MATCH: [a/b/c.d/e.f]

0

这里有类似的东西。你可以传递一个回调函数来过滤文件:

package main

import (
   "io/fs"
   "path/filepath"
)

func glob(root string, fn func(string)bool) []string {
   var files []string
   filepath.WalkDir(root, func(s string, d fs.DirEntry, e error) error {
      if fn(s) {
         files = append(files, s)
      }
      return nil
   })
   return files
}

func main() {
   files := glob(".", func(s string) bool {
      return filepath.Ext(s) == ".txt"
   })
   for _, file := range files {
      println(file)
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接