Walk a directory
Walk a tree with filepath.WalkDir, which passes a DirEntry and avoids a stat per file. Return fs.SkipDir to prune, and symlinks are not followed.
A program has to visit every file below a directory, skipping some folders such as .git.
package main
import (
"fmt"
"io/fs"
"os"
"path/filepath"
)
func main() {
err := filepath.WalkDir(os.Args[1], func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() && d.Name() == ".git" {
return fs.SkipDir
}
if !d.IsDir() {
fmt.Println(path)
}
return nil
})
fmt.Println("err:", err)
}mkdir -p tree/.git tree/src
touch tree/readme tree/.git/config tree/src/main.go
ln -s src tree/link
go run main.go tree
echo ---
go run main.go nopetree/link
tree/readme
tree/src/main.go
err: <nil>
---
err: lstat nope: no such file or directoryWalkDir visits entries in lexical order and hands the callback a DirEntry that already holds the type, so no Lstat call is made for each file, which is why it is faster than the older filepath.Walk. Returning fs.SkipDir from a directory skips its contents. The link is listed as a file because the walk does not follow symbolic links, and a missing root reaches the callback as an error first. See the filepath documentation.
WalkDir needs Go 1.16. It does not follow links, so to descend into one resolve it with filepath.EvalSymlinks yourself and guard against loops. Returning the error stops the whole walk; return nil to carry on past an unreadable directory. Changes made to the tree while it is being walked may or may not be seen.