List all files in a directory in Go
Yesterday, I was working on a small script that was supposed to list all .md files inside a directory.
Given this directory tree:
.
├── andrei.md
├── kata_one.md
├── kata_two.md
└── sub
├── another
│ └── kata_four.md
└── kata_three.md
3 directories, 4 filesI wanted a []string containing the absolute path of each .md file found inside my directory as below:
/home/andrei/Projects/katas/kata_one.md
/home/andrei/Projects/katas/kata_two.md
/home/andrei/Projects/katas/sub/another/kata_four.md
/home/andrei/Projects/katas/sub/kata_three.mYou’ll notice andrei.md is missing from the list because andrei.md is a directory, not a file.
Let’s see what options we have to achieve this result.
Using fs.Glob: search by pattern
Since I need all .md files under dir, I can create a filesystem for the tree of files rooted inside dir by using os.DirFS and then do a pattern match for *.md files with fs.Glob:
package main
import (
"fmt"
"io/fs"
"log"
"os"
"path"
)
func main() {
dir := "/home/andrei/Projects/katas"
files := listFiles(dir)
for _, v := range files {
fmt.Println(v)
}
}
func listFiles(dir string) []string {
root := os.DirFS(dir)
mdFiles, err := fs.Glob(root, "*.md")
if err != nil {
log.Fatal(err)
}
var files []string
for _, v := range mdFiles {
files = append(files, path.Join(dir, v))
}
return files
}Using this approach, I get the following output:
/home/andrei/Projects/katas/andrei.md
/home/andrei/Projects/katas/kata_one.md
/home/andrei/Projects/katas/kata_two.mdAn approach with the same result would be to use filepath.Glob directly:
func listFiles(dir string) []string {
entries, err := filepath.Glob(dir + "/*.md")
if err != nil {
log.Fatal(err)
}
var files []string
for _, v := range entries {
files = append(files, filepath.Join(dir, v))
}
return files
}There are 2 problems with these approaches:
- It only lists entries from the current directory. It doesn’t search for files inside child directories.
- In my case
/home/andrei/Projects/katas/andrei.mdis a directory path, not a file path. Since I don’t need directories I need to filter them out.
Let’s see another example where we can filter out directories.
Using os.ReadDir: list all directory entries
Here’s an example using os.ReadDir which reads the named directory, returning all its entries([]DirEntry) sorted by filename. A DirEntry is an interface with a small number of utility methods.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
)
func main() {
dir := "/home/andrei/Projects/katas"
files := listFiles(dir)
for _, v := range files {
fmt.Println(v)
}
}
func listFiles(dir string) []string {
entries, err := os.ReadDir(dir)
if err != nil {
log.Fatal(err)
}
var files []string
for _, v := range entries {
if v.IsDir() {
continue
}
if filepath.Ext(v.Name()) != ".md" {
continue
}
files = append(files, filepath.Join(dir, v.Name()))
}
return files
}We can check if an entry is a directory with v.IsDir(). So we can filter out the andrei.md directory, getting this result:
/home/andrei/Projects/katas/kata_one.md
/home/andrei/Projects/katas/kata_two.mdAnother function worth mentioning filepath.Ext() which is used to check the filename extension. Don’t forget to add the dot in the extension: checking for md instead of .md will not work!
Get Andrei Boar’s stories in your inbox
Join Medium for free to get updates from this writer.
The directory is filtered out, but we still miss files from the subdirectories. Let’s see how to fix that in the next section.
Using filePath.WalkDir: recursive search
WalkDir recursively walks the directory tree, thus checking child directories and files. For each entry in the directory, it calls a function in which we can filter out what we don’t need and add the files to the result:
package main
import (
"fmt"
"io/fs"
"log"
"path/filepath"
)
func main() {
dir := "/home/andrei/Projects/katas"
files := listFiles(dir)
for _, v := range files {
fmt.Println(v)
}
}
func listFiles(dir string) []string {
var files []string
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if !d.IsDir() && filepath.Ext(path) == ".md" {
files = append(files, path)
}
return nil
})
if err != nil {
log.Fatal(err)
}
return files
}Now we get the intended result:
/home/andrei/Projects/katas/kata_one.md
/home/andrei/Projects/katas/kata_two.md
/home/andrei/Projects/katas/sub/another/kata_four.md
/home/andrei/Projects/katas/sub/kata_three.mdConclusion
Here’s a quick guideline on when to use which:
- use
os.ReadDirwhen you quickly want to list direct entries inside a directory. - use
fs.Globwhen you need to list files with the same extension and you’re 100% sure you don’t have directories named with that extension. - use
filePath.WalkDirwhen you want to recursively search inside a directory tree.
That’s all!
If you learned something new please give a 👏 or bookmark this article for when you need to list some files. If I missed something feel free to leave a comment or connect with me on Linkedin.
Happy coding!








