⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Command-Line Arguments

const std = @import("std");
const expect = std.testing.expect;
const expectEqualStrings = std.testing.expectEqualStrings;

test "iterate arguments" {
    const gpa = std.testing.allocator;
    // A real program iterates `init.minimal.args` (see the page). Here we
    // parse a fixed command line with the same iterator, so the test stays
    // deterministic in the browser sandbox, which has no argv of its own.
    var it = try std.process.Args.IteratorGeneral(.{}).init(gpa, "app --name zig -v");
    defer it.deinit();

    try expectEqualStrings("app", it.next().?); // argv[0] is the program name
    try expectEqualStrings("--name", it.next().?);
    try expectEqualStrings("zig", it.next().?);
    try expectEqualStrings("-v", it.next().?);
    try expect(it.next() == null); // exhausted
}

test "a minimal flag parser" {
    const gpa = std.testing.allocator;
    var it = try std.process.Args.IteratorGeneral(.{}).init(gpa, "app --verbose --out result.txt");
    defer it.deinit();

    _ = it.next(); // skip argv[0]
    var verbose = false;
    var out: []const u8 = "a.out";
    while (it.next()) |arg| {
        if (std.mem.eql(u8, arg, "--verbose")) {
            verbose = true;
        } else if (std.mem.eql(u8, arg, "--out")) {
            // A flag that takes a value pulls the next token itself.
            out = it.next() orelse return error.MissingValue;
        }
    }
    try expect(verbose);
    try expectEqualStrings("result.txt", out);
}

test "environment variables" {
    const gpa = std.testing.allocator;
    // `init.environ_map` is exactly this type, filled from the real
    // environment. Building one by hand keeps the test self-contained.
    var env = std.process.Environ.Map.init(gpa);
    defer env.deinit();
    try env.put("EDITOR", "vim");

    try expectEqualStrings("vim", env.get("EDITOR").?);
    try expect(env.get("MISSING") == null);
    try expect(env.contains("EDITOR"));
}

Where the real arguments come from

Your main receives a std.process.Init (the same value that carries io, gpa, and the arena). Its minimal.args field is a std.process.Args, and iterate turns it into an iterator whose next yields each argument as a [:0]const u8:

pub fn main(init: std.process.Init) !void {
    var it = init.minimal.args.iterate();
    defer it.deinit();

    _ = it.next(); // argv[0]: the program's own path
    while (it.next()) |arg| {
        // ... handle arg ...
    }
}

The snippet above uses Args.IteratorGeneral, which parses a fixed string with the identical next shape. That is what lets it run in your browser: the WASI sandbox has no argv of its own, so a real iterator would come back empty. The parsing logic you write is the same either way.

Parsing is yours to write

There is no argparse in the standard library. For a small tool the loop in the snippet, match a flag, and pull the next token when it takes a value, is usually all you need. The first argument is always the program path, so skip it before parsing. Reach for a package (see the Build System section) only when you want subcommands, help text, and typed conversion.

Environment variables

init.environ_map is a std.process.Environ.Map built from the real environment. It behaves like the other maps in this section: get returns an optional, contains a bool. Because absence is normal for an environment variable, get returning null is the case you handle, not an error.

Spawning other programs

The reverse direction, running a subprocess, is std.process.Child. Like everything that touches the outside world it takes an Io, and it lets you wire up the child’s stdin, stdout, and stderr or capture its output. It needs a real OS process, so it is not something the browser sandbox can demonstrate.