Skip to main content
Go provides several functions for working with directories in the file system, from creating and removing directories to listing contents and walking directory trees.

Creating Directories

Single Directory

Create a single directory with os.Mkdir:
package main

import "os"

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// Create directory with permissions 0755
	err := os.Mkdir("subdir", 0755)
	check(err)
}
The second argument to os.Mkdir is the permission mode (0755 means read/write/execute for owner, read/execute for others).

Directory Hierarchy

Create a hierarchy of directories including parents with os.MkdirAll:
// Create parent directories if they don't exist
err := os.MkdirAll("subdir/parent/child", 0755)
check(err)
os.MkdirAll is similar to the command-line mkdir -p - it creates all necessary parent directories.

Removing Directories

Clean Up After Use

// Remove directory and all contents
defer os.RemoveAll("subdir")
os.RemoveAll deletes the entire directory tree, similar to rm -rf. Use with caution!
Use defer to ensure temporary directories are cleaned up, even if errors occur.

Reading Directory Contents

Use os.ReadDir to list directory contents:
import "fmt"

// Read directory entries
c, err := os.ReadDir("subdir/parent")
check(err)

fmt.Println("Listing subdir/parent")
for _, entry := range c {
	fmt.Println(" ", entry.Name(), entry.IsDir())
}
os.ReadDir returns a slice of os.DirEntry objects, which provide efficient access to file information.

Changing Directory

Use os.Chdir to change the current working directory:
// Change to subdirectory
err = os.Chdir("subdir/parent/child")
check(err)

// Now read current directory
c, err = os.ReadDir(".")
check(err)

fmt.Println("Listing subdir/parent/child")
for _, entry := range c {
	fmt.Println(" ", entry.Name(), entry.IsDir())
}

// Change back to original directory
err = os.Chdir("../../..")
check(err)
Changing directories affects the entire process. Be careful when using os.Chdir in concurrent programs.

Walking Directory Trees

Use filepath.WalkDir to recursively visit all files and directories:
import (
	"io/fs"
	"path/filepath"
)

fmt.Println("Visiting subdir")
err = filepath.WalkDir("subdir", visit)
check(err)

// Visit function called for each file/directory
func visit(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	fmt.Println(" ", path, d.IsDir())
	return nil
}
The visit function is called for every file and directory encountered, including the root directory.

Complete Example

package main

import (
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// Create directory
	err := os.Mkdir("subdir", 0755)
	check(err)
	defer os.RemoveAll("subdir")

	// Helper to create empty files
	createEmptyFile := func(name string) {
		d := []byte("")
		check(os.WriteFile(name, d, 0644))
	}

	createEmptyFile("subdir/file1")

	// Create directory hierarchy
	err = os.MkdirAll("subdir/parent/child", 0755)
	check(err)

	createEmptyFile("subdir/parent/file2")
	createEmptyFile("subdir/parent/file3")
	createEmptyFile("subdir/parent/child/file4")

	// List directory contents
	c, err := os.ReadDir("subdir/parent")
	check(err)

	fmt.Println("Listing subdir/parent")
	for _, entry := range c {
		fmt.Println(" ", entry.Name(), entry.IsDir())
	}

	// Change directory
	err = os.Chdir("subdir/parent/child")
	check(err)

	c, err = os.ReadDir(".")
	check(err)

	fmt.Println("Listing subdir/parent/child")
	for _, entry := range c {
		fmt.Println(" ", entry.Name(), entry.IsDir())
	}

	err = os.Chdir("../../..")
	check(err)

	// Walk directory tree
	fmt.Println("Visiting subdir")
	err = filepath.WalkDir("subdir", visit)
	check(err)
}

func visit(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	fmt.Println(" ", path, d.IsDir())
	return nil
}

Common Operations

// Single directory
os.Mkdir("mydir", 0755)

// With parents
os.MkdirAll("path/to/mydir", 0755)

Advanced Walking

Skip Directories

filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	
	// Skip .git directories
	if d.IsDir() && d.Name() == ".git" {
		return filepath.SkipDir
	}
	
	fmt.Println(path)
	return nil
})

Filter by File Type

filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	
	// Only process .go files
	if !d.IsDir() && filepath.Ext(path) == ".go" {
		fmt.Println(path)
	}
	
	return nil
})

DirEntry vs FileInfo

os.DirEntry is more efficient than os.FileInfo for directory listings because it avoids unnecessary system calls.
// Get full FileInfo when needed
entries, _ := os.ReadDir(".")
for _, entry := range entries {
	// Efficient: Name and IsDir are cached
	fmt.Println(entry.Name(), entry.IsDir())
	
	// Get full FileInfo only if needed
	info, _ := entry.Info()
	fmt.Println(info.Size(), info.ModTime())
}

Best Practices

Prefer os.MkdirAll over os.Mkdir when creating directories. It safely handles existing directories and creates parent directories as needed.
Use defer os.RemoveAll() to ensure temporary directories are cleaned up, even if errors occur.
Always check and handle errors in filepath.WalkDir callback functions. Return the error to stop walking or filepath.SkipDir to skip a directory.
  • 0755 for directories (rwxr-xr-x): owner can read/write/execute, others can read/execute
  • 0700 for private directories (rwx------): only owner can access
os.Chdir affects the entire process. In concurrent programs, use absolute paths instead of changing directories.
Call entry.Info() only when you need full file information. Basic operations like Name() and IsDir() are more efficient.

Directory Operations Reference

os.Mkdir

Create a single directory

os.MkdirAll

Create directory hierarchy with parents

os.Remove

Remove empty directory

os.RemoveAll

Remove directory and all contents

os.ReadDir

List directory contents

os.Chdir

Change current directory

filepath.WalkDir

Recursively walk directory tree

os.Getwd

Get current working directory

File Paths

Learn about portable file path operations

Temporary Files

Working with temporary files and directories

Build docs developers (and LLMs) love