List and Filter Files in a Directory in Zig

List top-level files and return results matching a filter, no recursion.

This recipe was written using Zig version 0.15.2. If this recipe is outdated, please let me know on X (@sepyke) or Bluesky (@pyk.sh).

Recently, I was building a simple library and needed to list all Zig files in an examples directory to build them as binaries in my build.zig. This recipe shows how to use std.fs.Dir.iterate() to list files in a single directory and filter them.

Similar to Dir.walk(), you first need a directory handle. You can get this by using std.fs.cwd() for the current working directory and then openDir() to access your target subdirectory (e.g. “examples”). Just like with walk(), it’s important to ensure .iterate = true is set in OpenOptions when opening the directory to allow its contents to be read.

The Dir.iterate() function returns an Iterator that goes through the immediate contents of a directory. It does not traverse into subdirectories, making it a more direct and efficient option when you only need top-level files.

Here is the code snippet with the inline comments:

list_files_in_directory.zig
const std = @import("std");
const fs = std.fs;
const mem = std.mem;
const Allocator = mem.Allocator;

pub fn main() !void {
    // In a real `build.zig`, `fs.cwd()` would typically be your project root.
    // For this demonstration, we'll use a temporary directory to keep things clean.
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup(); // Clean up the temporary directory when done.
    const base_dir = tmp.dir;

    // Create the 'examples' directory and some files inside it for testing.
    try base_dir.makeDir("examples");
    try base_dir.writeFile(.{ .sub_path = "examples/main.zig", .data = "const a = 1;" });
    try base_dir.makePath("examples/utils"); // Create a subdirectory
    try base_dir.writeFile(.{ .sub_path = "examples/utils/helper.zig", .data = "const b = 2;" }); // This one won't be found by `iterate()`
    try base_dir.writeFile(.{ .sub_path = "examples/README.md", .data = "docs" }); // A non-Zig file

    const sub_dir_to_scan = "examples";
    std.debug.print("Searching for .zig files non-recursively in subdirectory: '{s}'\n", .{sub_dir_to_scan});

    // Open the target subdirectory with iteration enabled.
    var target_dir = try base_dir.openDir(sub_dir_to_scan, .{
        .iterate = true,
    });
    defer target_dir.close(); // Always remember to close directory handles!

    std.debug.print("Found files:\n", .{});
    var iter = target_dir.iterate();
    while (try iter.next()) |entry| {
        // We only care about files that end with ".zig".
        // `entry.kind == .file` checks if it's a file, not a directory.
        if (entry.kind == .file and mem.endsWith(u8, entry.name, ".zig")) {
            std.debug.print("file: {s}\n", .{entry.name});
        }
    }
}

Make sure the zig is intalled:

Shell
$ zig version
0.15.2

Then run it:

Shell
$ zig run list_files_in_directory.zig
Searching for .zig files non-recursively in subdirectory: 'examples'
Found files:
file: main.zig

Other recipes in File System