@std/process

API Reference

class ChildProcess

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): Bool

Send 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());
Run in Playground
fn readStderr(buf: ByteBuffer, offset: Int, len: Int): Int

Blocking 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(): String

Drain 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): Int

Blocking 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());
Run in Playground
fn readStdoutText(): String

Drain 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 {
}
Run in Playground
fn wait(): Int

Block 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): Int

Write 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());
Run in Playground
fn writeStdinText(text: String): Int

Write 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.

class Process

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"));
Run in Playground
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);
Run in Playground
fn chdir(path: String): Bool

Change 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));
Run in Playground
fn cwd(): String

Current working directory as an absolute path.

returns — cwd string.

import { Process } from "@std/process";

print(Process.cwd().length() > 0);
Run in Playground
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"));
Run in Playground
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(): Int

Host 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?): ProcessOutput

Run 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());
Run in Playground
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());
Run in Playground
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?): ChildProcess

Spawn 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());
Run in Playground
fn stdioMode(s: Stdio): Int

Encode 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.

class ProcessOutput

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(): String

Buffered 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());
Run in Playground
fn stdoutText(): String

Buffered stdout decoded as UTF-8.

returns — stdout text.

import { Process } from "@std/process";

let out = Process.run("echo", ["hi"]);
print(out.stdoutText());
Run in Playground
class SpawnOptions

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
enum Stdio
  • Inherit
  • Null
  • Piped