🐨

Zigでコマンドライン引数を扱うサンプルプログラム

2023/02/11に公開

普通の方法

std.process.argsAlloc() を使用します。
[:0]const u8 から []const u8 を得るにはstd.mem.sliceTo()を使用します。

iadd.zig
const std = @import("std");

pub fn main() !void {
    const alc = std.heap.page_allocator;
    const args = try std.process.argsAlloc(alc);
    defer std.process.argsFree(alc, args);

    if (args.len < 2) {
        std.debug.print("Usage: {s} decimal_numbers ..\n", .{args[0]});
        std.os.exit(1);
    }
    var sum: i32 = 0;
    for (args[1..]) |a| {
        const arg = std.mem.sliceTo(a, 0);
        const num = try std.fmt.parseInt(i32, arg, 10);
        sum += num;
    }
    const stdout = std.io.getStdOut().writer();
    try stdout.print("{d}\n", .{sum});
}
$ zig build-exe iadd.zig 
$ ./iadd
Usage: ./iadd decimal_numbers ..
$ ./iadd 1 2 3
6

allocしない方法

以下のように書けば、Windows環境で動作しなくなりますが、Allocatorを使用せずに済みます。
引数の個数が最初にわからないので、Usageの表示の判断のためにArgIteratorを回すときにarg_numをインクリメントしています。

iadd_no_alloc.zig
const std = @import("std");

pub fn main() !void {
    var ai = std.process.args();
    const cmd_name = std.mem.sliceTo(ai.next().?, 0);
    var sum: i32 = 0;
    var arg_num: i32 = 0;
    while (ai.next()) |a| {
        const arg = std.mem.sliceTo(a, 0);
        const num = try std.fmt.parseInt(i32, arg, 10);
        sum += num;
        arg_num += 1;
    }
    if (arg_num == 0) {
        std.debug.print("Usage: {s} decimal_numbers ..\n", .{cmd_name});
        std.os.exit(1);
    }
    const stdout = std.io.getStdOut().writer();
    try stdout.print("{d}\n", .{sum});
}
$ zig build-exe iadd_no_alloc.zig
$ ./iadd_no_alloc 
Usage: ./iadd_no_alloc decimal_numbers ..
$ ./iadd_no_alloc 1 2 3 4
10

コマンドラインオプションなどもっと高度なことをするには

まだ試していませんが、このあたりのライブラリを使うのがよさそうです。
https://github.com/Hejsil/zig-clap

Zigの公式版パッケージマネージャができたら、こういうのを導入してみたいです。

Discussion