Listing Directories in Go: From Simple to Advanced
\n\nWorking with the file system is one of the fundamental tasks of any programming language. In Go, it is implemented so elegantly that even a routine operation like list directory in Go turns into an exciting adventure. If you've ever wondered, "How do I get a list of files in a folder in Go?" — you've come to the right place. Today, we'll cover all possible methods, from simple output to recursive traversal with filtering.
\n\nWhy is this topic so popular on Stack Overflow? Because Go offers several approaches at once, and choosing the right one is half the battle. We'll look at the classic os.ReadDir, the deprecated ioutil.ReadDir (though you can still find it in older projects), and the powerful filepath.Walk for recursive traversal. Let's go!
1. Simple Listing: os.ReadDir
\n\nStarting with Go 1.16, the os package introduced a new method ReadDir, which has become the de facto standard. It returns a slice of os.DirEntry objects — lightweight descriptions of files and folders. Here's how it looks:
package main\n\nimport (\n "fmt"\n "os"\n "log"\n)\n\nfunc main() {\n // Read the contents of the current directory\n entries, err := os.ReadDir(".")\n if err != nil {\n log.Fatal(err)\n }\n\n fmt.Println("Contents of the current folder:")\n for _, entry := range entries {\n fmt.Printf("- %s (is directory: %v)\\n", entry.Name(), entry.IsDir())\n }\n}\n\n\nThis code will output all files and folders in the current directory. Note: os.ReadDir does not read subfolders — only the top level. If you need to list directory in Go recursively, read on.
2. ioutil.ReadDir: Old but Familiar
\n\nBefore Go 1.16, everyone used ioutil.ReadDir. It returns []os.FileInfo — a heavier structure with full file information (size, permissions, modification time). Although the method is marked as deprecated, you can still find it in legacy code:
package main\n\nimport (\n "fmt"\n "io/ioutil"\n "log"\n)\n\nfunc main() {\n files, err := ioutil.ReadDir("./mydir")\n if err != nil {\n log.Fatal(err)\n }\n\n fmt.Println("Files in mydir (old way):")\n for _, file := range files {\n fmt.Printf("Name: %s, Size: %d bytes\\n", file.Name(), file.Size())\n }\n}\n\n\nThe main difference: ioutil.ReadDir sorts the result by file name (like ls), while os.ReadDir does not. If order matters, use sort.Slice or manual sorting.
3. Recursive Traversal with filepath.Walk
\n\nWhen you need to go through all subfolders, filepath.Walk comes to the rescue. This function traverses the directory tree, calling a callback for each found item. Perfect for backups, file searches, or analyzing project structure:
package main\n\nimport (\n "fmt"\n "os"\n "path/filepath"\n)\n\nfunc main() {\n root := "./myproject"\n err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n if err != nil {\n return err\n }\n fmt.Printf("Found: %s (size: %d)\\n", path, info.Size())\n return nil\n })\n if err != nil {\n fmt.Println("Traversal error:", err)\n }\n}\n\n\nAn important nuance: filepath.Walk follows symlinks and can enter an infinite loop if there are cyclic references. For production, use filepath.WalkDir (Go 1.16+) — it is lighter and safer.
4. Filtering: Only .go Files or Only Folders
\n\nOften you need to get not everything, but only specific file types. Let's implement filtering using strings.HasSuffix and the IsDir() check.