Skip to content
shellhacks.dev
Go

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.

2 min read
Summarize in AI

A program has to visit every file below a directory, skipping some folders such as .git.

main.goGo
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)
}
Shell
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 nope
Text
tree/link
tree/readme
tree/src/main.go
err: <nil>
---
err: lstat nope: no such file or directory

WalkDir 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.

Share

More snippets

Go

Set a timeout for a command

Stop a command that takes too long with exec.CommandContext and context.WithTimeout. In Go 1.19 a shell's child can keep Output() waiting past the deadline.

2 min read
Go

Read command line flags

Read command line options with the flag package: flag.String, flag.Int and flag.Bool, then flag.Parse(). Parsing stops at the first argument that is not a flag.

2 min read
Go

Raise the scanner line limit

Raise the line limit of bufio.Scanner with Buffer(make([]byte, 0, 64*1024), max). Lines over 64 KB otherwise stop the scan with token too long.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.