# Read command line arguments

**URL:** <https://ziggit.dev/t/read-command-line-arguments/220>\
**Category:** Help\
**Created:** [June 22, 2022, 9:47am UTC](https://ziggit.dev/t/read-command-line-arguments/220 "2022-06-22T09:47:25Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bane](https://ziggit.dev/letter_avatar_proxy/v4/letter/b/65b543/32.png) [@Bane](https://ziggit.dev/u/Bane)\
**Post date:** [June 22, 2022, 9:47am UTC](https://ziggit.dev/t/read-command-line-arguments/220/1 "2022-06-22T09:47:25Z")

</div>

Hi,

I’m looking for a simple way to read the command line arguments, passed to my program. I don’t want to use a full fledged command line parser, since this program is for personal use only and I hope to learn zig a little more with this approach.

Is there a zig equivalent to `argc` and `*argv[]` like in C?

---

<div class="post-metadata">

**Author:** ![gonzo](https://ziggit.dev/user_avatar/ziggit.dev/gonzo/32/54_2.png) [@gonzo](https://ziggit.dev/u/gonzo)\
**Post date:** [June 22, 2022, 2:45pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/2 "2022-06-22T14:45:46Z")

</div>

Take a look at `std.process.args()` and related. The source files for zig’s standard library are very easy to follow. Mine are located at `homebrew/Cellar/zig/0.9.1/lib/zig/std`.

---

<div class="post-metadata">

**Author:** ![Bane](https://ziggit.dev/letter_avatar_proxy/v4/letter/b/65b543/32.png) [@Bane](https://ziggit.dev/u/Bane)\
**Post date:** [June 22, 2022, 6:09pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/3 "2022-06-22T18:09:01Z")

</div>

thank you for your reply.  
I struggle to do it right. To be honest, I’m a little bit frustrated, I’m sure this is an easy task, but the lack of documentation makes this really hard.

```zig
var args = std.process.args(); //why does this only compile with "var"??
_ = args.skip(); //to skip the zig call

const allocator = std.heap.page_allocator;
const file_path = try args.next(allocator); 
...

```

but the compiler want’s me to remove the try before `args.next(allocator)`, but why?

---

<div class="post-metadata">

**Author:** ![PrajwalCH](https://ziggit.dev/user_avatar/ziggit.dev/prajwalch/32/122_2.png) [@PrajwalCH](https://ziggit.dev/u/PrajwalCH)\
**Post date:** [June 23, 2022, 4:09pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/4 "2022-06-23T16:09:58Z")

</div>

Note: English is not my native language therefore i always struggled to write English but i tried my best to explain you if you get confused let me know 🙂

Ok let me explain to you why it only compiles using var by a simple example.

```zig
const Student = struct {
    name: []const u8,

    pub fn init() Student {
        return Student{.name = undefined};
    }

    pub fn setName(self: *Student, name: []const u8) void {
        self.name = name;
};

pub fn main() !void {
    var student1 = Student.init();
    student1.setName("foo");
}

```

In the above example method `setName` sets the member `name` therefore inorder to change the member of itself it needs to take a mutable pointer to itself. But if you decalre a `student1` with const it will be become immutable so when you call `student1.setName` it will be passed as `*const Student` (immutable) where `setName` expects `*Student` (mutable).

In this case `std.process.args()` returns a `ArgIterator` [here is the actual code of it](https://github.com/ziglang/zig/blob/master/lib/std/process.zig#L735)

For second problem: Why compiler wants you to remove try keyword?

try keyword is used when a function returns either an error or a value. Which means when you use `const val = try someFun()` it basically means “if someFunc() returns an error catch it and return it to the caller otherwise store the actual value to val”

In your case `next()` returns an optional value.  
So to unwrap the actual value you can do `const file_path = args.next().?;` Notice `.?` at the end which basically means if next() returns a value give me that value otherwise panic with the message `attempt to use null value`

I hope i explain to you properly.

Doc Links:  
[try keyword](https://ziglang.org/documentation/master/#try)  
[Optionals](https://ziglang.org/documentation/master/#Optionals)

---

<div class="post-metadata">

**Author:** ![jmc](https://ziggit.dev/user_avatar/ziggit.dev/jmc/32/38_2.png) [@jmc](https://ziggit.dev/u/jmc)\
**Post date:** [June 28, 2022, 4:22pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/5 "2022-06-28T16:22:41Z")

</div>

An alternative is to use [`std.process.argsAlloc()`](https://ziglang.org/documentation/master/std/#std;process.argsAlloc) so that you can allocate a copy of the argv as an array of strings, which might be easier to iterate over.

---

<div class="post-metadata">

**Author:** ![sam701](https://ziggit.dev/user_avatar/ziggit.dev/sam701/32/189_2.png) [@sam701](https://ziggit.dev/u/sam701)\
**Post date:** [July 13, 2022, 6:44pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/6 "2022-07-13T18:44:46Z")

</div>

I wrote a library for argument parsing: [GitHub - sam701/zig-cli: A simple package for building command line apps in Zig](https://github.com/sam701/zig-cli) The design is inspired by the urfave/cli golang library.

---

<div class="post-metadata">

**Author:** ![ratfactor](https://ziggit.dev/user_avatar/ziggit.dev/ratfactor/32/48_2.png) [@ratfactor](https://ziggit.dev/u/ratfactor)\
**Post date:** [November 20, 2023, 6:19pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/7 "2023-11-20T18:19:41Z")

</div>

Sorry to play necromancer on this thread, but this forum post continues to be the first thing that comes up when I search the Web for “zig args”, so I thought it would be nice to have a simple, complete, working example.

## No allocations (won’t work on Windows or WASI)

Uses `std.os.argv` (prepopulated on program startup):

```zig
const std = @import("std");

pub fn main() !void {
    std.debug.print("There are {d} args:\n", .{std.os.argv.len});
    for(std.os.argv) |arg| {
        std.debug.print(" {s}\n", .{arg});
    }
}

```

## Or with allocations

This uses `std.process.argsAlloc()` to parse the args into a handy array of strings:

```zig
const std = @import("std");

pub fn main() !void {
    // Get allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    // Parse args into string array (error union needs 'try')
    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    // Get and print them!
    std.debug.print("There are {d} args:\n", .{args.len});
    for(args) |arg| {
        std.debug.print(" {s}\n", .{arg});
    }
}

```

## Building and running either version:

As separate steps:

```bash
$ zig build-exe arg-test.zig

$ ./arg-test foo bar baz
There are 4 args:
  ./arg-test
  foo
  bar
  baz

```

As a single step (what I usually do while developing):

```bash
$ zig run arg-test.zig -- foo bar baz
There are 4 args:
  /home/dave/.cache/zig/o/2679ea75d45ed3a8c27da28ac64a89b4/arg-test
  foo
  bar
  baz

```

---

<div class="post-metadata">

**Author:** ![slonik-az](https://ziggit.dev/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@slonik-az](https://ziggit.dev/u/slonik-az)\
**Post date:** [November 20, 2023, 7:43pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/8 "2023-11-20T19:43:31Z")

</div>

If you are on Linux or MacOS you can use [std.os.argv](https://ziglang.org/documentation/master/std/#A;std:os.argv) that is an array of zero-terminated strings. No need for allocations.

---

<div class="post-metadata">

**Author:** ![nurpax](https://ziggit.dev/user_avatar/ziggit.dev/nurpax/32/1217_2.png) [@nurpax](https://ziggit.dev/u/nurpax)\
**Post date:** [November 20, 2023, 8:23pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/9 "2023-11-20T20:23:04Z")

</div>

It’s not a big loss though to do a few allocs once per execution of the CLI app to keep it portable across platforms?

---

<div class="post-metadata">

**Author:** ![slonik-az](https://ziggit.dev/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@slonik-az](https://ziggit.dev/u/slonik-az)\
**Post date:** [November 20, 2023, 8:43pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/10 "2023-11-20T20:43:23Z")

</div>

To each their own. 🙂 Modern Windows (v10, v11) come with Linux VM under the name WSL (Windows Subsystem Linux). Reduces the pressure to support Windows API.

---

<div class="post-metadata">

**Author:** ![ratfactor](https://ziggit.dev/user_avatar/ziggit.dev/ratfactor/32/48_2.png) [@ratfactor](https://ziggit.dev/u/ratfactor)\
**Post date:** [November 20, 2023, 8:46pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/11 "2023-11-20T20:46:14Z")

</div>

Great point! (And that’s what I’d personally want most of the time, too.) Thanks!  
Editing my answer to include that example as well so people have options.

---

<div class="post-metadata">

**Author:** ![MarvellouslyExist](https://ziggit.dev/letter_avatar_proxy/v4/letter/m/ea666f/32.png) [@MarvellouslyExist](https://ziggit.dev/u/MarvellouslyExist)\
**Post date:** [October 18, 2024, 9:04pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/12 "2024-10-18T21:04:27Z")

</div>

Here’s a bit of code I and ChatGPT wrote together to parse some simple command line arguments. I wrote it to learn some zig.

```zig
const ParsedArgs = struct {
    num_words: u64,
    seed: u64,
    capitalize: bool,
};

const ArgParseError = error{
    MissingArgument,
    InvalidNumber,
    InvalidSeed,
    UnknownArgument,
    PrintHelp,
};

fn parseArgs(allocator: Allocator) !ParsedArgs {
    const args = std.process.argsAlloc(allocator) catch |err| {
        std.debug.print("Failed to allocate arguments\n", .{});
        return err;
    };

    var num_words: u64 = DEFAULT_NUM_WORDS;
    var seed: u64 = @intCast(std.time.milliTimestamp());
    var capitalize = false;

    var i: usize = 1;
    while (i < args.len) : (i += 1) {
        const arg = args[i];
        if (std.mem.eql(u8, arg, "-n") or std.mem.eql(u8, arg, "--num-words")) {
            i += 1;
            if (i >= args.len) {
                std.debug.print("Missing argument for -n\n", .{});
                return ArgParseError.MissingArgument;
            }
            num_words = std.fmt.parseInt(u64, args[i], 10) catch {
                std.debug.print("Invalid number for -n\n", .{});
                return ArgParseError.InvalidNumber;
            };
        } else if (std.mem.eql(u8, arg, "-s") or std.mem.eql(u8, arg, "--seed")) {
            i += 1;
            if (i >= args.len) {
                std.debug.print("Missing argument for -s\n", .{});
                return ArgParseError.MissingArgument;
            }
            seed = std.fmt.parseInt(u64, args[i], 10) catch {
                std.debug.print("Invalid seed for -s\n", .{});
                return ArgParseError.InvalidSeed;
            };
        } else if (std.mem.eql(u8, arg, "-c") or std.mem.eql(u8, arg, "--capitalize")) {
            capitalize = true;
        } else if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
            printHelp() catch |err| {
                std.debug.print("Failed to print help\n", .{});
                return err;
            };
            return ArgParseError.PrintHelp;
        } else {
            std.debug.print("Unknown argument: {s}\n", .{arg});
            return ArgParseError.UnknownArgument;
        }
    }

    const parsed_args = ParsedArgs{
        .num_words = num_words,
        .seed = seed,
        .capitalize = capitalize,
    };

    return parsed_args;
}

```

---

<div class="post-metadata">

**Author:** ![emacstheviking](https://ziggit.dev/user_avatar/ziggit.dev/emacstheviking/32/5115_2.png) [@emacstheviking](https://ziggit.dev/u/emacstheviking)\
**Post date:** [April 26, 2025, 10:45pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/13 "2025-04-26T22:45:21Z")

</div>

Nailed it for me, absolutely nailed it. I needed to iterate the args as filenames, I injected an index to skip [0], the exe name, and then the remaining loop iterations I now run through my file load-all and processing.  
THanks!

---

<div class="post-metadata">

**Author:** ![ratrogue](https://ziggit.dev/user_avatar/ziggit.dev/ratrogue/32/9514_2.png) [@ratrogue](https://ziggit.dev/u/ratrogue)\
**Post date:** [July 11, 2026, 7:15pm UTC](https://ziggit.dev/t/read-command-line-arguments/220/14 "2026-07-11T19:15:49Z")

</div>

Coming from 2026. Found this thread because I also had problems getting CL args, this is the working version in 0.16 (thanks to @evaleek on Discord):

```zig
pub fn main(init: std.process.Init) !void {
    var args_iter = try init.minimal.args.iterateAllocator(init.gpa);
    defer args_iter.deinit();

    var i: usize = 0;
    while (args_iter.next()) |arg| {
        std.debug.print("arg {d}: \"{s}\"\n", .{ i, arg });
        i += 1;
    }
}

```
