本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
Golang递归获取目录下所有文件方法实例
我是陈晓 · 146浏览 · 发布于2023-02-27 +关注

这篇文章主要给大家介绍了关于Golang递归获取目录下所有文件的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

1.问题

如果我想获取一个目录下的所有文件列表,使用 Golang 该如何实现呢?

比如有个目录 dir 结构如下:

tree dir
dir
├── bar.txt
├── foo.txt
└── subdir
    └── baz.txt

那么如何获取 dir 目录下的所有文件路径呢?

dir/foo.txt
dir/bar.txt
dir/subdir/baz.txt

2.io/ioutil

标准库 io/ioutil 包提供了一个函数 ReadDir() 可以获取指定目录下的所有内容,按文件名排序,返回 []fs.FileInfo 切片来描述目录中的所有内容。

func ReadDir(dirname string) ([]fs.FileInfo, error)

利用 ioutil.ReadDir() 我们可以获取目录中的所有文件吗?

// ListDir lists all the file or dir names in the specified directory.
// Note that ListDir don't traverse recursively.
func ListDir(dirname string) ([]string, error) {
    infos, err := ioutil.ReadDir(dirname)
    if err != nil {
        return nil, err
    }
    names := make([]string, len(infos))
    for i, info := range infos {
        names[i] = info.Name()
    }
    return names, nil
}

我们来测试一下:

package main
 import (
    "fmt"
    "io/ioutil"
)
 func main() {
    names, _ := ListDir("dir")
    fmt.Printf("names:%v\n", names)
}

运行输出:

names:[bar.txt foo.txt subdir]

可见 ioutil.ReadDir() 并不会递归获取子目录的内容。

3.递归获取

如果想递归获子目录的内容,该如何实现呢?

我们可以递归的调用我们自己的函数,来递归遍历子目录。

// GetDirAllFilePaths gets all the file paths in the specified directory recursively.
func GetDirAllFilePaths(dirname string) ([]string, error) {
    // Remove the trailing path separator if dirname has.
    dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
     infos, err := ioutil.ReadDir(dirname)
    if err != nil {
        return nil, err
    }
     paths := make([]string, 0, len(infos))
    for _, info := range infos {
        path := dirname + string(os.PathSeparator) + info.Name()
        if info.IsDir() {
            tmp, err := GetDirAllFilePaths(path)
            if err != nil {
                return nil, err
            }
            paths = append(paths, tmp...)
            continue
        }
        paths = append(paths, path)
    }
    return paths, nil
}

我们来测试一下:

package main
 import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)
 func main() {
    paths, _ := GetDirAllFilePaths("dir/")
    for _, path := range paths {
        fmt.Println(path)
    }
}

运行输出:

dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt

哇,看起来大功告成。但果真如此吗?

4.包含符号链接的情况

如果我们此时在目录 dir 中加入一个符号链接,指向另外一个目录,那结果会如何呢?

tree dir
dir
├── bar.txt
├── foo.txt
├── subdir
│   └── baz.txt
└── zipln -> ../zip

tree zip
zip
└── qux.txt

还是运行调用 GetDirAllFilePaths(),我们得到的结果如下:

dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln

可见,当子目录为符号链接时,我们并没有访问链接指向的目标文件。

我们改变一下实现,当子目录是符号链接时,读取目标目录下的文件。

// GetDirAllFilePathsFollowSymlink gets all the file paths in the specified 
directory recursively.
func GetDirAllFilePathsFollowSymlink(dirname string) ([]string, error) {
    // Remove the trailing path separator if dirname has.
    dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
     infos, err := ioutil.ReadDir(dirname)
    if err != nil {
        return nil, err
    }
     paths := make([]string, 0, len(infos))
    for _, info := range infos {
        path := dirname + string(os.PathSeparator) + info.Name()
        realInfo, err := os.Stat(path)
        if err != nil {
            return nil, err
        }
        if realInfo.IsDir() {
            tmp, err := GetDirAllFilePathFollowSymlink(path)
            if err != nil {
                return nil, err
            }
            paths = append(paths, tmp...)
            continue
        }
        paths = append(paths, path)
    }
    return paths, nil
}

我们来测试一下:

package main
 import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)
 func main() {
    paths, _ := GetDirAllFilePathsFollowSymlink("dir/")
    for _, path := range paths {
        fmt.Println(path)
    }
}

运行输出:

dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln/qux.txt

perfect,这就是我们想要的效果。

5.同时返回目录的路径

有时,我们还需要目录路径,即获取指定目录下的文件和子目录的路径。比如在对一个目录进行压缩时会需要。

还是以上文 dir 目录为例,我们想要的结果是:

dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt

我们只要稍微改造 GetDirAllFilePaths 和 GetDirAllFilePathsFollowSymlink 即可,在遍历时把当前的目录加入结果集。

并更名 GetDirAllFilePaths 为 GetDirAllEntryPaths,GetDirAllFilePathsFollowSymlink 为 GetDirAllEntryPathsFollowSymlink,因为条目(Entry)比文件(File)语义更符合函数的功能,因为不仅可以获取文件,也可以获取目录的路径。

// GetDirAllEntryPaths gets all the file or dir paths in the specified directory recursively.
// Note that GetDirAllEntryPaths won't follow symlink if the subdir is a symbolic link.
func GetDirAllEntryPaths(dirname string, incl bool) ([]string, error) {
    // Remove the trailing path separator if dirname has.
    dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
     infos, err := ioutil.ReadDir(dirname)
    if err != nil {
        return nil, err
    }
     paths := make([]string, 0, len(infos))
    // Include current dir.
    if incl {
        paths = append(paths, dirname)
    }
     for _, info := range infos {
        path := dirname + string(os.PathSeparator) + info.Name()
        if info.IsDir() {
            tmp, err := GetDirAllEntryPaths(path, incl)
            if err != nil {
                return nil, err
            }
            paths = append(paths, tmp...)
            continue
        }
        paths = append(paths, path)
    }
    return paths, nil
}
 // GetDirAllEntryPathsFollowSymlink gets all the file or dir paths in the specified 
 directory recursively.
func GetDirAllEntryPathsFollowSymlink(dirname string, incl bool) ([]string, error) {
    // Remove the trailing path separator if dirname has.
    dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
     infos, err := ioutil.ReadDir(dirname)
    if err != nil {
        return nil, err
    }
     paths := make([]string, 0, len(infos))
    // Include current dir.
    if incl {
        paths = append(paths, dirname)
    }
     for _, info := range infos {
        path := dirname + string(os.PathSeparator) + info.Name()
        realInfo, err := os.Stat(path)
        if err != nil {
            return nil, err
        }
        if realInfo.IsDir() {
            tmp, err := GetDirAllEntryPathsFollowSymlink(path, incl)
            if err != nil {
                return nil, err
            }
            paths = append(paths, tmp...)
            continue
        }
        paths = append(paths, path)
    }
    return paths, nil
}

我们测试一下。

func main() {
    fmt.Println("not follow symlink:")
    paths, _ := GetDirAllEntryPaths("dir/", true)
    for _, path := range paths {
        fmt.Println(path)
    }
     fmt.Println("\nfollow symlink:")
    paths, _ = GetDirAllEntryPathsFollowSymlink("dir/", true)
    for _, path := range paths {
        fmt.Println(path)
    }
}

运行输出:

not follow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
follow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt

6.go-huge-util

以上函数已放置开源库 go-huge-util,可 import 直接使用。

package main
 import (
    "github.com/dablelv/go-huge-util/file"
)
 func main() {
    // 获取目录下所有文件和子目录名称(不会递归)。
    names, _ := file.ListDir("dir")
     // 递归获取目录下所有文件路径(不解析符号链接)
    paths, _ := file.GetDirAllEntryPaths("dir", false)
    // 递归获取目录下所有文件和目录路径(不解析符号链接)
    paths, _ = file.GetDirAllEntryPaths("dir", true)
     // 递归获取目录下所有文件路径(解析符号链接)
    paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir", false)
    // 递归获取目录下所有文件与目录路径(解析符号链接)
    paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir/", true)
}

欢迎大家 Star & PR。


相关推荐

PHP实现部分字符隐藏

沙雕mars · 1325浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 908浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 970浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 962浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1086浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
我是一名在上海一家互联网公司上班,专注技术开发工作等。
小鸟云服务器
扫码进入手机网页