252 lines
6.2 KiB
Markdown
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?
|