递归查找目录中的文件

18

我想递归地在一个目录中查找符合特定模式的所有文件(包括子目录)。我编写了以下代码来实现这个功能:

libRegEx, e := regexp.Compile("^.+\\.(dylib)$")
if e != nil {
    log.Fatal(e)
}

files, err := ioutil.ReadDir("/usr/lib")
if err != nil {
    log.Fatal(err)
}

for _, f := range files {
    if libRegEx.MatchString(f.Name()) {
        println(f.Name())
    }
}

很遗憾,它只能在/usr/bin中搜索,但我也想在其子目录中搜索匹配项。我该如何做到这一点?谢谢。


11
请使用 https://golang.org/pkg/path/filepath/#Walk。 - Volker
4个回答

27

标准库的filepath包中包含Walk函数,正是为了这个目的而设计的:“Walk函数遍历以root为根的文件树,在树中的每个文件或目录上调用walkFn函数,包括根目录。” 例如:

libRegEx, e := regexp.Compile("^.+\\.(dylib)$")
if e != nil {
    log.Fatal(e)
}

e = filepath.Walk("/usr/lib", func(path string, info os.FileInfo, err error) error {
    if err == nil && libRegEx.MatchString(info.Name()) {
        println(info.Name())
    }
    return nil
})
if e != nil {
    log.Fatal(e)
}

7
您应该更新答案,使用在Go 1.16中可用的更高效的filepath.WalkDir函数。 - user15697984

25

从Go 1.16开始 (2021年2月),你可以使用filepath.WalkDir

package main

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

func walk(s string, d fs.DirEntry, err error) error {
   if err != nil {
      return err
   }
   if ! d.IsDir() {
      println(s)
   }
   return nil
}

func main() {
   filepath.WalkDir("..", walk)
}

3
如果您正在寻找不使用walk的内容,我发现了this project
尽管使用字符串,但主要的递归算法似乎非常有效。它基本上相当于以下代码,并且有点让我想起归并排序和其他递归算法:
func processed(fileName string, processedDirectories []string) bool {
    for i := 0; i < len(processedDirectories); i++ {
        if processedDirectories[i] != fileName {
            continue
        }
        return true
    }
    return false
}

func listDirContents(path string, dirs []string) {
    files, _ := ioutil.ReadDir(path)

    for _, f := range files {
        var newPath string
        if path != "/" {
            newPath = fmt.Sprintf("%s/%s", path, f.Name())
        } else {
            newPath = fmt.Sprintf("%s%s", path, f.Name())
        }

        if f.IsDir() {
            if !processed(newPath, dirs) {
                dirs = append(dirs, newPath)
                listDirContents(newPath, dirs)
            }
        } else {
            fmt.Println(newPath)
        }
    }
}

那实际上会打印出从提供的目录开始找到的所有路径,并包括所有子目录。因此,您需要检查路径是否包含目标字符串,而不仅仅是使用fmt.Println()语句打印路径。
尝试与find命令相比,它在约0.8秒内扫描了我的/home目录...find命令找到了相同的文件,但是速度快了约0.5秒(比上述算法快)。

为什么有人要使用一个不常见的库而不是Go内置的库呢? - Ted Brownlow
一个原因是只有在满足特定条件的情况下才能在目录中执行操作。例如,只有当存在README.md文件时才迭代每个文件,如果不存在,则继续执行。filepath.WalkDir没有并发安全的设置方式。 - dasper

-3

您可以使用以下代码来使用目录中的所有文件:

files, err := ioutil.ReadDir(dirPath)
check(err)

for _, file := range files {
    fmt.Println(dirPath + file.Name())
}

代码使用io/ioutil包来读取给定目录中的所有文件,然后循环遍历它们以打印它们的名称。

这样做不行。你只会在 dirPath 中找到文件/目录,而不是所有的文件。 - Cililing

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