API Reference
Live child process. Either `wait()` for the exit code or let the handle drop: dropping detaches (pipes close, child keeps running). Always `wait()` children you spawn; never leak running children. Handles are tracked in one host-wide table. `handle` is that table slot, not the OS pid; use `pid` for anything the host outside can see.
init(handle: Int, pid: Int)
fields
- handle: Int
- pid: Int
fn closeStdin()Close the child stdin pipe (sends EOF). Safe to call more than once. Programs reading stdin to EOF, such as `cat` or a filter, finish after this. Call it before `wait()` or the child may wait for input that never arrives.
fn kill(signal: Int): BoolSend a signal (15 SIGTERM by default, 9 SIGKILL).
signal — Unix signal number; ignored on Windows, which always force kills.
returns — true when the signal was delivered. False when the child has already been reaped or the handle is gone. Signal 15 lets a child run cleanup handlers; 9 does not. You still owe the child a `wait()` after killing it.
import { Process } from "@std/process";
let child = Process.spawn("sh", ["-c", "sleep 5"]);
print(child.kill());
print(child.wait());fn readStderr(buf: ByteBuffer, offset: Int, len: Int): IntBlocking read from the child stderr pipe into a buffer.
buf — destination buffer, written from `offset`.
offset — first byte to write.
len — capacity to use, or -1 for the rest of the buffer.
returns — bytes read (0 on EOF), or -1 when not piped. The stderr twin of `readStdout`. Reading both streams needs two buffers and careful ordering: a child that fills one pipe blocks until it is drained. `Process.run()` does the interleaving for you.
fn readStderrText(): StringDrain the child stderr pipe to EOF and decode as UTF-8.
returns — stderr text. Same draining behavior as `readStdoutText`, on stderr.
fn readStdout(buf: ByteBuffer, offset: Int, len: Int): IntBlocking read from the child stdout pipe into a buffer.
buf — destination buffer, written from `offset`.
offset — first byte to write.
len — capacity to use, or -1 for the rest of the buffer.
returns — bytes read (0 on EOF), or -1 when not piped. Blocks until the child writes or closes the stream. Returns -1 when stdout was not `Stdio.Piped` or the pipe is already consumed by `wait()`. To read a stream of unknown length, either loop until 0 or use `readStdoutText()`.
import { Process } from "@std/process";
import { ByteBuffer } from "@std/bytes";
let child = Process.spawn("echo", ["abc"]);
let buf = ByteBuffer.allocate(3);
print(child.readStdout(buf));
print(buf.readString(0, 3));
print(child.wait());fn readStdoutText(): StringDrain the child stdout pipe to EOF and decode as UTF-8.
returns — stdout text. Reads in 4096-byte chunks until the pipe closes, so it blocks as long as the child keeps stdout open. Returns whatever was read so far when the stream reports an error. Empty string when stdout was not piped.
fn tryWait(): Int?Non-blocking poll: exit code when the child finished, else null.
returns — code after exit, null while running. Returns as soon as the child is reaped and keeps returning that code, so a poll loop can check for null until it gets a value.
import { Process } from "@std/process";
let child = Process.spawn("sh", ["-c", "echo hi"]);
print(child.readStdoutText());
while child.tryWait() == null {
}fn wait(): IntBlock until the child exits, reap it, and close its pipes.
returns — exit code (128+signal when killed by a Unix signal). Also closes stdin first, so a child blocked on input gets EOF. Calling it twice returns the same code. Capture anything you need from the pipes before waiting: the pipes close on the way out.
fn writeStdin(buf: ByteBuffer, offset: Int, len: Int): IntWrite buffer bytes into the child stdin pipe.
buf — source buffer.
offset — first byte to send.
len — byte count, or -1 to send to the end of the buffer.
returns — bytes written, or -1 when stdin is closed. A short write is normal on a pipe with a small buffer; loop until the total matches the bytes you meant to send. Needs `Stdio.Piped` stdin, otherwise the first call reports -1.
import { Process } from "@std/process";
let child = Process.spawn("cat");
child.writeStdinText("ping\n");
child.closeStdin();
print(child.readStdoutText());
print(child.wait());fn writeStdinText(text: String): IntWrite text into the child stdin pipe.
text — encoded as UTF-8 and written in one call.
returns — bytes written, or -1 when stdin is closed. Same short-write caveat as `writeStdin`. The buffer is converted for the call, so no byte buffer is left for you to manage.
Host process surface: argv, environment, cwd, identity, exit, plus child spawning. Thin wrappers over `@std/env` where it already covers the call; new intrinsics only for the gaps.
fn allEnv(): Map<String, String>Snapshot of the whole environment.
returns — name-to-value map. A fresh map each call: edits to it do not touch the real environment. Use `setEnv()` for that. Entries whose names contain `=` are skipped, since they cannot be split unambiguously.
import { Process } from "@std/process";
Process.setEnv("COUNTED", "1");
print(Process.allEnv().get("COUNTED"));fn args(): Array<String>Command-line arguments including the program name at index 0.
returns — argument array.
import { Process } from "@std/process";
print(Process.args().length() > 0);fn chdir(path: String): BoolChange the working directory.
path — new working directory.
returns — true on success. Affects this process only. Relative paths resolve against the current directory, and a missing or unreadable directory returns false instead of failing the run.
import { Process } from "@std/process";
let before = Process.cwd();
print(Process.chdir("/tmp"));
print(Process.chdir(before));fn cwd(): StringCurrent working directory as an absolute path.
returns — cwd string.
import { Process } from "@std/process";
print(Process.cwd().length() > 0);fn env(key: String): String?Read an environment variable.
key — variable name.
returns — value when set, null otherwise. Unset is null here, while `Env.get()` answers with an empty string. The lookup walks a fresh snapshot of the environment, so it sees changes made by `setEnv()` earlier in the run.
import { Process } from "@std/process";
Process.setEnv("DEMO", "on");
print(Process.env("DEMO"));fn exit(code: Int)Terminate immediately with an exit code. Deferred blocks do not run.
code — exit code passed to the host. Nothing after this line runs, and buffered handles are not flushed by any runtime teardown.
fn flattenEnv(env: Map<String, String>): Array<String>Map an env table to `KEY=value` pairs for the spawn boundary.
env — name-to-value table.
returns — one `KEY=value` string per entry. A key mapped to null becomes `KEY=` with an empty value. Used by `spawn()` and `run()`; call it directly only to inspect what a table would produce.
fn pid(): IntHost process id.
returns — pid, always positive. The id of the running program, not of any child.
fn removeEnv(key: String)Remove an environment variable.
key — variable name. Removing a variable that was never set does nothing.
fn run(command: String, args: Array<String>, options: SpawnOptions?): ProcessOutputRun to completion: close stdin, drain both pipes concurrently, wait, and capture everything.
command — program to run, resolved through `PATH`.
args — arguments after the program name.
options — spawn configuration, or null for the defaults.
returns — exit code plus buffered stdout/stderr. The child gets EOF on stdin right away. stdout is drained on a helper thread while stderr is drained inline, so neither pipe can fill and stall the child; then the child is waited on. Inherited or null streams are not captured and arrive as empty buffers. Output is buffered whole, so a child that never stops writing grows the host's memory. For long output, use `spawn()` and read in chunks. A missing program aborts the process; so does a command outside the `sys:exec` grant, which stops with `[S401]`. `args` is never run through a shell, so quoting is your job.
import { Process } from "@std/process";
let out = Process.run("sh", ["-c", "echo hi; echo bad 1>&2; exit 3"]);
print(out.exitCode);
print(out.stdoutText());
print(out.stderrText());import { Process, Stdio, SpawnOptions } from "@std/process";
let opts = new SpawnOptions(null, null, Stdio.Piped, Stdio.Null, Stdio.Piped);
let out = Process.run("sh", ["-c", "echo hidden; echo shown 1>&2"], opts);
print(out.stdoutText().length());
print(out.stderrText());fn setEnv(key: String, value: String)Write an environment variable for this process and its children.
key — variable name.
value — new value. This process sees the change immediately. Children spawned afterwards do not inherit it: spawn clears the environment and forwards `PATH` plus the table passed in `SpawnOptions.env`.
fn spawn(command: String, args: Array<String>, options: SpawnOptions?): ChildProcessSpawn a child without waiting. Caller owns waiting via the handle.
command — program to run, resolved through `PATH`.
args — arguments after the program name.
options — spawn configuration, or null for the defaults.
returns — live child handle. With no options, all three streams are piped, which means `wait()` alone can deadlock on a chatty child: it blocks on a full pipe while the parent waits. Drain the pipes first, or use `Process.run()`. A missing program aborts the process; so does a command outside the `sys:exec` grant, which stops with `[S401]`. `args` is never run through a shell, so quoting is your job.
import { Process } from "@std/process";
let child = Process.spawn("echo", ["streamed"]);
print(child.readStdoutText());
print(child.wait());fn stdioMode(s: Stdio): IntEncode one stdio wire mode: inherit 0, piped 1, null 2.
s — stdio wiring to encode.
returns — wire number passed to the spawn intrinsic. This is the translation layer for `SpawnOptions`. Rarely needed directly.
Captured result of `Process.run()`: exit code plus buffered stdout/stderr. Buffers are exact-size and owned by this object. Only `Stdio.Piped` streams are captured; an inherited or null stream contributes an empty buffer.
init(exitCode: Int, stdout: ByteBuffer, stderr: ByteBuffer)
fields
- exitCode: Int
- stderr: ByteBuffer
- stdout: ByteBuffer
fn stderrText(): StringBuffered stderr decoded as UTF-8.
returns — stderr text.
import { Process } from "@std/process";
let out = Process.run("sh", ["-c", "echo oops 1>&2"]);
print(out.stderrText());fn stdoutText(): StringBuffered stdout decoded as UTF-8.
returns — stdout text.
import { Process } from "@std/process";
let out = Process.run("echo", ["hi"]);
print(out.stdoutText());Spawn configuration: working directory, environment overrides merged over the inherited environment, and per-stream stdio wiring. Defaults when no options are passed: all three streams piped, no working directory change, and no environment entries beyond `PATH`. ```rnx import { Process, Stdio, SpawnOptions } from "@std/process"; import { Map } from "@std/collections"; let env = new Map<String, String>(); env.set("GREETING", "hi"); let opts = new SpawnOptions(null, env, Stdio.Piped, Stdio.Piped, Stdio.Piped); let out = Process.run("sh", ["-c", "echo $GREETING"], opts); print(out.stdoutText()); ```
init(cwd: String?, env: Map<String, String>?, stdin: Stdio, stdout: Stdio, stderr: Stdio)
fields
- cwd: String?
- env: Map<String, String>?
- stderr: Stdio
- stdin: Stdio
- stdout: Stdio
- Inherit
- Null
- Piped