Golang遞歸獲取目錄下所有文件方法實(shí)例
1.問(wèn)題
如果我想獲取一個(gè)目錄下的所有文件列表,使用 Golang 該如何實(shí)現(xiàn)呢?
比如有個(gè)目錄 dir 結(jié)構(gòu)如下:
tree dir
dir
├── bar.txt
├── foo.txt
└── subdir
└── baz.txt
那么如何獲取 dir 目錄下的所有文件路徑呢?
dir/foo.txt
dir/bar.txt
dir/subdir/baz.txt
2.io/ioutil
標(biāo)準(zhǔn)庫(kù) io/ioutil 包提供了一個(gè)函數(shù) ReadDir()
可以獲取指定目錄下的所有內(nèi)容,按文件名排序,返回 []fs.FileInfo
切片來(lái)描述目錄中的所有內(nèi)容。
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 }
我們來(lái)測(cè)試一下:
package main import ( "fmt" "io/ioutil" ) func main() { names, _ := ListDir("dir") fmt.Printf("names:%v\n", names) }
運(yùn)行輸出:
names:[bar.txt foo.txt subdir]
可見(jiàn) ioutil.ReadDir() 并不會(huì)遞歸獲取子目錄的內(nèi)容。
3.遞歸獲取
如果想遞歸獲子目錄的內(nèi)容,該如何實(shí)現(xiàn)呢?
我們可以遞歸的調(diào)用我們自己的函數(shù),來(lái)遞歸遍歷子目錄。
// 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 }
我們來(lái)測(cè)試一下:
package main import ( "fmt" "io/ioutil" "os" "strings" ) func main() { paths, _ := GetDirAllFilePaths("dir/") for _, path := range paths { fmt.Println(path) } }
運(yùn)行輸出:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
哇,看起來(lái)大功告成。但果真如此嗎?
4.包含符號(hào)鏈接的情況
如果我們此時(shí)在目錄 dir 中加入一個(gè)符號(hào)鏈接,指向另外一個(gè)目錄,那結(jié)果會(huì)如何呢?
tree dir
dir
├── bar.txt
├── foo.txt
├── subdir
│ └── baz.txt
└── zipln -> ../ziptree zip
zip
└── qux.txt
還是運(yùn)行調(diào)用 GetDirAllFilePaths(),我們得到的結(jié)果如下:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln
可見(jiàn),當(dāng)子目錄為符號(hào)鏈接時(shí),我們并沒(méi)有訪(fǎng)問(wèn)鏈接指向的目標(biāo)文件。
我們改變一下實(shí)現(xiàn),當(dāng)子目錄是符號(hào)鏈接時(shí),讀取目標(biāo)目錄下的文件。
// 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 }
我們來(lái)測(cè)試一下:
package main import ( "fmt" "io/ioutil" "os" "strings" ) func main() { paths, _ := GetDirAllFilePathsFollowSymlink("dir/") for _, path := range paths { fmt.Println(path) } }
運(yùn)行輸出:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln/qux.txt
perfect,這就是我們想要的效果。
5.同時(shí)返回目錄的路徑
有時(shí),我們還需要目錄路徑,即獲取指定目錄下的文件和子目錄的路徑。比如在對(duì)一個(gè)目錄進(jìn)行壓縮時(shí)會(huì)需要。
還是以上文 dir 目錄為例,我們想要的結(jié)果是:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt
我們只要稍微改造 GetDirAllFilePaths 和 GetDirAllFilePathsFollowSymlink 即可,在遍歷時(shí)把當(dāng)前的目錄加入結(jié)果集。
并更名 GetDirAllFilePaths 為 GetDirAllEntryPaths,GetDirAllFilePathsFollowSymlink 為 GetDirAllEntryPathsFollowSymlink,因?yàn)闂l目(Entry)比文件(File)語(yǔ)義更符合函數(shù)的功能,因?yàn)椴粌H可以獲取文件,也可以獲取目錄的路徑。
// 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 }
我們測(cè)試一下。
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) } }
運(yùn)行輸出:
not follow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/ziplnfollow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt
6.go-huge-util
以上函數(shù)已放置開(kāi)源庫(kù) go-huge-util,可 import 直接使用。
package main import ( "github.com/dablelv/go-huge-util/file" ) func main() { // 獲取目錄下所有文件和子目錄名稱(chēng)(不會(huì)遞歸)。 names, _ := file.ListDir("dir") // 遞歸獲取目錄下所有文件路徑(不解析符號(hào)鏈接) paths, _ := file.GetDirAllEntryPaths("dir", false) // 遞歸獲取目錄下所有文件和目錄路徑(不解析符號(hào)鏈接) paths, _ = file.GetDirAllEntryPaths("dir", true) // 遞歸獲取目錄下所有文件路徑(解析符號(hào)鏈接) paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir", false) // 遞歸獲取目錄下所有文件與目錄路徑(解析符號(hào)鏈接) paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir/", true) }
歡迎大家 Star & PR。
參考文獻(xiàn)
總結(jié)
到此這篇關(guān)于Golang遞歸獲取目錄下所有文件的文章就介紹到這了,更多相關(guān)Golang遞歸獲取目錄所有文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Gin golang web開(kāi)發(fā)模型綁定實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Gin golang web開(kāi)發(fā)模型綁定實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Golang實(shí)現(xiàn)帶優(yōu)先級(jí)的select
這篇文章主要為大家詳細(xì)介紹了如何在Golang中實(shí)現(xiàn)帶優(yōu)先級(jí)的select,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定的幫助,需要的可以參考一下2023-04-04Go編程中常見(jiàn)錯(cuò)誤和不良實(shí)踐解析
這篇文章主要為大家介紹了Go編程中常見(jiàn)錯(cuò)誤和不良實(shí)踐解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Go中的 panic / recover 簡(jiǎn)介與實(shí)踐記錄
這篇文章主要介紹了Go中的 panic / recover 簡(jiǎn)介與實(shí)踐,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04GO語(yǔ)言文件的創(chuàng)建與打開(kāi)實(shí)例分析
這篇文章主要介紹了GO語(yǔ)言文件的創(chuàng)建與打開(kāi)的具體用法,實(shí)例分析了GO語(yǔ)言文件創(chuàng)建與打開(kāi)操作中所涉及的函數(shù)具體用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12golang的time包:秒、毫秒、納秒時(shí)間戳輸出方式
這篇文章主要介紹了golang的time包:秒、毫秒、納秒時(shí)間戳輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12深入了解Go項(xiàng)目標(biāo)準(zhǔn)目錄布局
本文主要介紹了Go項(xiàng)目標(biāo)準(zhǔn)目錄布局,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05