The standard library gives access to process information through std.process.
A process can inspect its command-line arguments.
const std = @import("std");
pub fn main() !void {
var args = try std.process.argsWithAllocator(std.heap.page_allocator);
defer args.deinit();
while (args.next()) |arg| {
std.debug.print("{s}\n", .{arg});
}
}Run it:
zig run main.zig -- one two threeThe output is similar to:
/path/to/main
one
two
threeThe first argument is the program name.
The remaining arguments are the words passed after --.
The -- separates arguments for zig run from arguments for the program.
argsWithAllocator uses an allocator because argument handling may need memory on some platforms.
var args = try std.process.argsWithAllocator(allocator);The iterator must be released.
defer args.deinit();For small examples, the page allocator is enough.
std.heap.page_allocatorFor larger programs, use the same allocator policy as the rest of the program.
A simple argument parser can be written directly.
const std = @import("std");
pub fn main() !void {
var args = try std.process.argsWithAllocator(std.heap.page_allocator);
defer args.deinit();
_ = args.next();
const name = args.next() orelse "world";
std.debug.print("hello, {s}\n", .{name});
}Run it:
zig run main.zig -- zigThe output is:
hello, zigRun it without an argument:
zig run main.zigThe output is:
hello, worldEnvironment variables are also available through std.process.
const std = @import("std");
pub fn main() !void {
const home = std.process.getEnvVarOwned(
std.heap.page_allocator,
"HOME",
) catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
std.debug.print("HOME is not set\n", .{});
return;
},
else => return err,
};
defer std.heap.page_allocator.free(home);
std.debug.print("{s}\n", .{home});
}getEnvVarOwned returns an allocated string.
The caller owns it and must free it.
defer allocator.free(home);The environment variable may not exist, so the error is handled explicitly.
The process module also provides access to the current working directory.
const std = @import("std");
pub fn main() !void {
var buffer: [std.fs.max_path_bytes]u8 = undefined;
const path = try std.process.getCwd(buffer[0..]);
std.debug.print("{s}\n", .{path});
}The buffer is supplied by the caller.
The function writes the current path into the buffer and returns the slice that was used.
This avoids allocation.
A program can also exit with a status code.
const std = @import("std");
pub fn main() void {
std.process.exit(1);
}Exit status 0 means success.
A nonzero status means failure by convention.
For command-line tools, this matters. Shell scripts and build systems inspect exit codes.
A program can use this pattern:
const std = @import("std");
pub fn main() !void {
const ok = false;
if (!ok) {
std.debug.print("error: operation failed\n", .{});
std.process.exit(1);
}
}The program prints a diagnostic and exits with failure.
Child processes are also managed through the standard library.
const std = @import("std");
pub fn main() !void {
var child = std.process.Child.init(
&.{ "zig", "version" },
std.heap.page_allocator,
);
const term = try child.spawnAndWait();
std.debug.print("{any}\n", .{term});
}Child.init takes an argument vector.
&.{ "zig", "version" }The first string is the program to run.
The rest are its arguments.
spawnAndWait starts the process and waits for it to finish.
The result describes how the child ended.
A more useful program captures output.
const std = @import("std");
pub fn main() !void {
const result = try std.process.Child.run(.{
.allocator = std.heap.page_allocator,
.argv = &.{ "zig", "version" },
});
defer std.heap.page_allocator.free(result.stdout);
defer std.heap.page_allocator.free(result.stderr);
std.debug.print("stdout: {s}", .{result.stdout});
std.debug.print("stderr: {s}", .{result.stderr});
}Child.run allocates buffers for standard output and standard error.
The caller frees both.
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);This makes process execution convenient while keeping ownership visible.
The process API follows the same rules as the rest of Zig.
Arguments are explicit.
Allocation is explicit.
Failure is explicit.
Exit status is explicit.
Exercise 14-25. Print all command-line arguments.
Exercise 14-26. Write a program that accepts a name and prints a greeting.
Exercise 14-27. Read an environment variable and handle the missing case.
Exercise 14-28. Run zig version as a child process and print its output.