* docs: improved language in yaml loader example * docs(api/http): fix typo * docs(api/spawn): fix typo * docs(api/transpiler): fix typo
7.1 KiB
Spawn child processes with Bun.spawn or Bun.spawnSync.
Spawn a process
Provide a command as an array of strings. The result of Bun.spawn() is a Bun.Subprocess object.
Bun.spawn(["echo", "hello"]);
The second argument to Bun.spawn is a parameters object that can be used to configure the subprocess.
const proc = Bun.spawn(["echo", "hello"], {
cwd: "./path/to/subdir", // specify a working direcory
env: { ...process.env, FOO: "bar" }, // specify environment variables
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
proc.pid; // process ID of subprocess
Input stream
By default, the input stream of the subprocess is undefined; it can be configured with the stdin parameter.
const proc = Bun.spawn(["cat"], {
stdin: await fetch(
"https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js",
),
});
const text = await new Response(proc.stdout).text();
console.log(text); // "const input = "hello world".repeat(400); ..."
{% table %}
null- Default. Provide no input to the subprocess
"pipe"- Return a
FileSinkfor fast incremental writing
"inherit"- Inherit the
stdinof the parent process
Bun.file()- Read from the specified file.
TypedArray | DataView- Use a binary buffer as input.
Response- Use the response
bodyas input.
Request- Use the request
bodyas input.
number- Read from the file with a given file descriptor.
{% /table %}
The "pipe" option lets incrementally write to the subprocess's input stream from the parent process.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // return a FileSink for writing
});
// enqueue string data
proc.stdin!.write("hello");
// enqueue binary data
const enc = new TextEncoder();
proc.stdin!.write(enc.encode(" world!"));
// send buffered data
proc.stdin!.flush();
// close the input stream
proc.stdin!.end();
Output streams
You can read results from the subprocess via the stdout and stderr properties. By default these are instances of ReadableStream.
const proc = Bun.spawn(["echo", "hello"]);
const text = await new Response(proc.stdout).text();
console.log(text); // => "hello"
Configure the output stream by passing one of the following values to stdout/stderr:
{% table %}
"pipe"- Default for
stdout. Pipe the output to aReadableStreamon the returnedSubprocessobject.
"inherit"- Default for
stderr. Inherit from the parent process.
Bun.file()- Write to the specified file.
null- Write to
/dev/null.
number- Write to the file with the given file descriptor.
{% /table %}
Exit handling
Use the onExit callback to listen for the process exiting or being killed.
const proc = Bun.spawn(["echo", "hello"], {
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
For convenience, the exited property is a Promise that resolves when the process exits.
const proc = Bun.spawn(["echo", "hello"]);
await proc.exited; // resolves when process exit
proc.killed; // boolean — was the process killed?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
To kill a process:
const proc = Bun.spawn(["echo", "hello"]);
proc.kill();
proc.killed; // true
proc.kill(); // specify an exit code
The parent bun process will not terminate until all child processes have exited. Use proc.unref() to detach the child process from the parent.
const proc = Bun.spawn(["echo", "hello"]);
proc.unref();
Blocking API
Bun provides a synchronous equivalent of Bun.spawn called Bun.spawnSync. This is a blocking API that supports the same inputs and parameters as Bun.spawn. It returns a SyncSubprocess object, which differs from Subprocess in a few ways.
- It contains a
successproperty that indicates whether the process exited with a zero exit code. - The
stdoutandstderrproperties are instances ofBufferinstead ofReadableStream. - There is no
stdinproperty. UseBun.spawnto incrementally write to the subprocess's input stream.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout!.toString());
// => "hello\n"
As a rule of thumb, the asynchronous Bun.spawn API is better for HTTP servers and apps, and Bun.spawnSync is better for building command-line tools.
Benchmarks
{%callout%}
⚡️ Under the hood, Bun.spawn and Bun.spawnSync use posix_spawn(3).
{%/callout%}
Bun's spawnSync spawns processes 60% faster than the Node.js child_process module.
$ bun spawn.mjs
cpu: Apple M1 Max
runtime: bun 0.2.0 (arm64-darwin)
benchmark time (avg) (min … max) p75 p99 p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi 888.14 µs/iter (821.83 µs … 1.2 ms) 905.92 µs 1 ms 1.03 ms
$ node spawn.node.mjs
cpu: Apple M1 Max
runtime: node v18.9.1 (arm64-darwin)
benchmark time (avg) (min … max) p75 p99 p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms 2.52 ms
Reference
interface Bun {
spawn(command: string[], options?: SpawnOptions): Subprocess;
spawnSync(command: string[], options?: SpawnOptions): SyncSubprocess;
}
interface SpawnOptions {
cwd?: string;
env?: Record<string, string>;
stdin?:
| "pipe"
| "inherit"
| "ignore"
| ReadableStream
| BunFile
| Blob
| Response
| Request
| number
| null;
stdout?:
| "pipe"
| "inherit"
| "ignore"
| BunFile
| TypedArray
| DataView
| null;
stderr?:
| "pipe"
| "inherit"
| "ignore"
| BunFile
| TypedArray
| DataView
| null;
onExit?: (
proc: Subprocess,
exitCode: number | null,
signalCode: string | null,
error: Error | null,
) => void;
}
interface Subprocess {
readonly pid: number;
readonly stdin?: number | ReadableStream | FileSink;
readonly stdout?: number | ReadableStream;
readonly stderr?: number | ReadableStream;
readonly exited: Promise<number>;
readonly exitCode: number | undefined;
readonly signalCode: Signal | null;
readonly killed: boolean;
ref(): void;
unref(): void;
kill(code?: number): void;
}
interface SyncSubprocess {
readonly pid: number;
readonly success: boolean;
readonly stdout: Buffer;
readonly stderr: Buffer;
}
type Signal =
| "SIGABRT"
| "SIGALRM"
| "SIGBUS"
| "SIGCHLD"
| "SIGCONT"
| "SIGFPE"
| "SIGHUP"
| "SIGILL"
| "SIGINT"
| "SIGIO"
| "SIGIOT"
| "SIGKILL"
| "SIGPIPE"
| "SIGPOLL"
| "SIGPROF"
| "SIGPWR"
| "SIGQUIT"
| "SIGSEGV"
| "SIGSTKFLT"
| "SIGSTOP"
| "SIGSYS"
| "SIGTERM"
| "SIGTRAP"
| "SIGTSTP"
| "SIGTTIN"
| "SIGTTOU"
| "SIGUNUSED"
| "SIGURG"
| "SIGUSR1"
| "SIGUSR2"
| "SIGVTALRM"
| "SIGWINCH"
| "SIGXCPU"
| "SIGXFSZ"
| "SIGBREAK"
| "SIGLOST"
| "SIGINFO";