【问题标题】:Go find files in directory recursively递归查找目录中的文件
【发布时间】:2017-11-13 14:23:01
【问题描述】:

我想在一个目录中递归查找所有匹配特定模式的文件(包括子目录)。我为此编写了代码:

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 中搜索,但我也想在其子目录中搜索匹配项。我怎样才能做到这一点?谢谢。

【问题讨论】:

标签: file search recursion go


【解决方案1】:

从 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)
}

【讨论】:

    【解决方案2】:

    标准库的filepath 包包含Walk 正是为了这个目的:“Walk 遍历以根为根的文件树,为树中的每个文件或目录调用 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)
    }
    

    【讨论】:

    • 您应该更新答案以使用 Go 1.16 中更有效的 filepath.WalkDir 函数。
    【解决方案3】:

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

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

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

    【讨论】:

    • 这行不通。您只会在 dirPath 中找到文件/目录,而不是所有文件。
    【解决方案4】:

    如果你正在寻找不使用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.3 秒内完成(快了整整 0.5 秒)比上面的算法)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-19
      • 1970-01-01
      • 2010-10-24
      • 2012-11-06
      • 1970-01-01
      • 2013-10-10
      • 2012-03-12
      • 1970-01-01
      相关资源
      最近更新 更多