# std.process - Process API Reference (Zig 0.16.0) Primary release-note source: https://ziglang.org/download/0.16.0/release-notes.html Zig 0.16 moves process I/O, args, environment, current directory, and child process management behind explicit `std.Io` and Juicy Main. ## Juicy Main Preferred application entry: ```zig const std = @import("std"); pub fn main(init: std.process.Init) !void { const gpa = init.gpa; const io = init.io; const arena = init.arena.allocator(); const args = try init.minimal.args.toSlice(arena); const env = init.environ_map; const preopens = init.preopens; _ = .{ gpa, io, args, env, preopens }; } ``` `std.process.Init` provides: - `minimal.args` - `minimal.environ` - `arena` - `gpa` - `io` - `environ_map` - `preopens` Use `std.process.Init.Minimal` only when a program deliberately wants less setup. ## Args Prefer args from `std.process.Init`: ```zig const args = try init.minimal.args.toSlice(init.arena.allocator()); ``` Avoid older global argument APIs in new 0.16 code unless you are inside compatibility code. ## Environment Prefer `init.environ_map` at application boundaries. Important error rename: - `error.EnvironmentVariableNotFound` -> `error.EnvironmentVariableMissing` When spawning, pass an environment map through process options: ```zig const result = try std.process.run(gpa, io, .{ .argv = &.{ "tool" }, .environ_map = init.environ_map, }); defer gpa.free(result.stdout); defer gpa.free(result.stderr); ``` ## Current Directory ```zig var buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; const n = try std.process.currentPath(io, &buffer); const cwd = buffer[0..n]; ``` ```zig const cwd = try std.process.currentPathAlloc(io, gpa); defer gpa.free(cwd); ``` Do not add new `std.process.getCwd*` callsites. ## Run and Capture Output Use `std.process.run(gpa, io, options)`. ```zig const result = try std.process.run(gpa, io, .{ .argv = &.{ "git", "status", "--short" }, .stdout_limit = .limited(64 * 1024), .stderr_limit = .limited(64 * 1024), .cwd = .inherit, }); defer gpa.free(result.stdout); defer gpa.free(result.stderr); switch (result.term) { .exited => |code| if (code != 0) return error.CommandFailed, else => return error.CommandFailed, } ``` Important options: - `argv` - `stdout_limit` / `stderr_limit` as `std.Io.Limit` - `reserve_amount` - `cwd` - `environ_map` - `expand_arg0` - `progress_node` - `create_no_window` - `disable_aslr` - `timeout` ## Spawn Child Process Use `std.process.spawn(io, options)`. `std.process.Child.init` is not the 0.16 pattern. ```zig var child = try std.process.spawn(io, .{ .argv = &.{ "tool", "--flag" }, .stdin = .ignore, .stdout = .pipe, .stderr = .pipe, .cwd = .inherit, }); defer child.kill(io); const term = try child.wait(io); _ = term; ``` `child.wait(io)` blocks until termination and cleans resources. `child.kill(io)` is uncancelable and idempotent after wait/kill. ## Pipes Pipe fields are `std.Io.File` values when requested. ```zig var child = try std.process.spawn(io, .{ .argv = &.{ "cat" }, .stdin = .pipe, .stdout = .pipe, .stderr = .pipe, }); defer child.kill(io); var stdin_buf: [4096]u8 = undefined; var stdin_writer = child.stdin.?.writer(io, &stdin_buf); try stdin_writer.interface.writeAll("hello\n"); try stdin_writer.interface.flush(); child.stdin.?.close(io); child.stdin = null; var stdout_buf: [4096]u8 = undefined; var stdout_reader = child.stdout.?.reader(io, &stdout_buf); const stdout = try stdout_reader.interface.allocRemaining(gpa, .limited(64 * 1024)); defer gpa.free(stdout); const term = try child.wait(io); _ = term; ``` For simultaneous stdout/stderr capture, prefer `std.process.run` or `std.Io.File.MultiReader` to avoid pipe deadlocks. ## Working Directory Child cwd uses `std.process.Child.Cwd`: ```zig .cwd = .inherit .cwd = .{ .path = "projects" } .cwd = .{ .dir = some_io_dir } ``` Use `std.process.spawnPath(io, dir, options)` when `argv[0]` should be resolved relative to a directory as a file path. ## Standard I/O Options `SpawnOptions.StdIo` values: - `.inherit` - `.file` - `.ignore` - `.pipe` - `.close` Example: ```zig var child = try std.process.spawn(io, .{ .argv = &.{ "tool" }, .stdin = .ignore, .stdout = .pipe, .stderr = .pipe, }); ``` ## Preopens WASI preopens moved to `std.process.Preopens` and are exposed by `std.process.Init`. ```zig const preopens = init.preopens; _ = preopens; ``` ## Memory Locking Memory locking/protection APIs moved under `std.process`: - `std.process.lockMemory` - `std.process.unlockMemory` - `std.process.lockMemoryAll` - `std.process.unlockMemoryAll` Use them only for explicit platform/security needs. ## Migration Map | Old pattern | Zig 0.16 pattern | |-------------|------------------| | `pub fn main() !void` plus global args/env | `pub fn main(init: std.process.Init) !void` | | `std.process.Child.run(.{ ... })` | `std.process.run(gpa, io, .{ ... })` | | `std.process.Child.init(argv, allocator)` | `std.process.spawn(io, .{ .argv = argv, ... })` | | `child.spawn()` | spawn returns the child | | `child.wait()` | `child.wait(io)` | | `child.kill()` | `child.kill(io)` | | `std.process.getCwd(...)` | `std.process.currentPath(io, ...)` | | `std.process.getCwdAlloc(...)` | `std.process.currentPathAlloc(io, allocator)` | | `error.EnvironmentVariableNotFound` | `error.EnvironmentVariableMissing` | ## Review Checklist - Does the function already have `std.process.Init` or an `io` parameter? - Are args/env taken from `init` instead of globals? - Are child processes bounded with output limits or timeouts where appropriate? - Are pipe reads/writes using `std.Io.File` readers/writers with `io`? - Is `child.kill(io)` used in `defer` when early exits could leave a process alive? - Is stdout/stderr capture safe from deadlock?