zig-skills/references/std-process.md

252 lines
6.2 KiB
Markdown

# 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);
```
On Windows these paths use WTF-8. On other platforms they are opaque path bytes
with no guaranteed text encoding; do not assume UTF-8 when displaying or
parsing them.
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`
Supplying `.environ_map` replaces the child's environment, but its `PATH` does
not resolve `argv[0]`; executable lookup still uses the parent environment.
Use `std.process.spawnPath` for deterministic directory-relative resolution.
## 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`
- `std.process.MemoryProtection`
- `std.process.protectMemory`
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?