使用 Bun.spawn
或 Bun.spawnSync
產生子程序。
產生程序 (Bun.spawn()
)
提供一個命令作為字串陣列。Bun.spawn()
的結果是一個 Bun.Subprocess
物件。
Bun.spawn(["echo", "hello"]);
Bun.spawn
的第二個參數是一個參數物件,可用於配置子程序。
const proc = Bun.spawn(["echo", "hello"], {
cwd: "./path/to/subdir", // specify a working directory
env: { ...process.env, FOO: "bar" }, // specify environment variables
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
proc.pid; // process ID of subprocess
輸入串流
預設情況下,子程序的輸入串流未定義;可以使用 stdin
參數進行配置。
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); ..."
null | 預設。不提供輸入給子程序 |
"pipe" | 傳回一個 FileSink 以快速增量寫入 |
"inherit" | 繼承父程序的 stdin |
Bun.file() | 從指定檔案讀取。 |
TypedArray | DataView | 使用二進制緩衝區作為輸入。 |
回應 | 使用回應 body 作為輸入。 |
請求 | 使用請求 body 作為輸入。 |
數字 | 從具有給定檔案描述符的檔案中讀取。 |
"pipe"
選項允許從父程序遞增寫入子程序的輸入串流。
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();
輸出串流
您可以透過 stdout
和 stderr
屬性從子程序中讀取結果。預設情況下,這些是 ReadableStream
的執行個體。
const proc = Bun.spawn(["echo", "hello"]);
const text = await new Response(proc.stdout).text();
console.log(text); // => "hello"
透過將下列值之一傳遞給 stdout/stderr
來設定輸出串流
"pipe" | stdout 的預設值。將輸出導管到已傳回 Subprocess 物件上的 ReadableStream 。 |
"inherit" | stderr 的預設值。從父程序繼承。 |
Bun.file() | 寫入指定的檔案。 |
null | 寫入 /dev/null 。 |
數字 | 寫入具有給定檔案描述符的檔案。 |
退出處理
使用 onExit
回呼來監聽程序退出或被終止。
const proc = Bun.spawn(["echo", "hello"], {
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
為了方便,exited
屬性是一個 Promise
,當程序退出時會解析。
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" | ...
若要終止程序
const proc = Bun.spawn(["echo", "hello"]);
proc.kill();
proc.killed; // true
proc.kill(); // specify an exit code
父 bun
程序不會終止,直到所有子程序都退出。使用 proc.unref()
將子程序從父程序分離。
const proc = Bun.spawn(["echo", "hello"]);
proc.unref();
程序間通訊 (IPC)
Bun 支援兩個 bun
程序之間的直接程序間通訊管道。若要接收來自已產生 Bun 子程序的訊息,請指定 ipc
處理常式。
注意 — 此 API 僅與其他 bun
程序相容。使用 process.execPath
來取得目前正在執行的 bun
可執行檔的路徑。
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* The message received from the sub process
**/
},
});
父程序可以使用已傳回 Subprocess
執行個體上的 .send()
方法將訊息傳送給子程序。傳送子程序的參考也可用作 ipc
處理常式中的第二個引數。
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* The message received from the sub process
**/
childProc.send("Respond to child")
},
});
childProc.send("I am your father"); // The parent can send messages to the child as well
同時,子程序可以使用 process.send()
向其父程序發送訊息,並使用 process.on("message")
接收訊息。這是 Node.js 中 child_process.fork()
使用的相同 API。
process.send("Hello from child as string");
process.send({ message: "Hello from child as object" });
process.on("message", (message) => {
// print message from parent
console.log(message);
});
// send a string
process.send("Hello from child as string");
// send an object
process.send({ message: "Hello from child as object" });
ipcMode
選項控制兩個程序之間的底層通信格式
advanced
:(預設)使用 JSCserialize
API 序列化訊息,它支援複製structuredClone
支援的所有內容。這不支援傳輸物件的所有權。json
:使用JSON.stringify
和JSON.parse
序列化訊息,它支援的物件類型不如advanced
多。
封鎖 API (Bun.spawnSync()
)
Bun 提供了一個 Bun.spawn
的同步等效項,稱為 Bun.spawnSync
。這是一個封鎖 API,支援與 Bun.spawn
相同的輸入和參數。它傳回一個 SyncSubprocess
物件,它在幾個方面與 Subprocess
不同。
- 它包含一個
success
屬性,表示程序是否以零退出碼退出。 stdout
和stderr
屬性是Buffer
的實例,而不是ReadableStream
。- 沒有
stdin
屬性。使用Bun.spawn
遞增寫入子程序的輸入串流。
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"
根據經驗法則,非同步 Bun.spawn
API 更適合 HTTP 伺服器和應用程式,而 Bun.spawnSync
更適合建立命令列工具。
基準測試
⚡️ 在底層,Bun.spawn
和 Bun.spawnSync
使用 posix_spawn(3)
。
Bun 的 spawnSync
產生程序的速度比 Node.js 的 child_process
模組快 60%。
bun spawn.mjs
cpu: Apple M1 Max
runtime: bun 1.x (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
參考
以下顯示 Spawn API 和類型的簡單參考。實際類型具有複雜的泛型,可以根據傳遞給 Bun.spawn
和 Bun.spawnSync
的選項,強烈地輸入 Subprocess
串流。有關完整詳細資訊,請在 bun.d.ts 中找到這些定義的類型。
interface Bun {
spawn(command: string[], options?: SpawnOptions.OptionsObject): Subprocess;
spawnSync(
command: string[],
options?: SpawnOptions.OptionsObject,
): SyncSubprocess;
spawn(options: { cmd: string[] } & SpawnOptions.OptionsObject): Subprocess;
spawnSync(
options: { cmd: string[] } & SpawnOptions.OptionsObject,
): SyncSubprocess;
}
namespace SpawnOptions {
interface OptionsObject {
cwd?: string;
env?: Record<string, string>;
stdin?: SpawnOptions.Readable;
stdout?: SpawnOptions.Writable;
stderr?: SpawnOptions.Writable;
onExit?: (
proc: Subprocess,
exitCode: number | null,
signalCode: string | null,
error: Error | null,
) => void;
}
type Readable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalent to "ignore"
| undefined // to use default
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalent to "ignore"
| undefined // to use default
| BunFile
| ArrayBufferView
| number
| ReadableStream
| Blob
| Response
| Request;
}
interface Subprocess<Stdin, Stdout, Stderr> {
readonly pid: number;
// the exact stream types here are derived from the generic parameters
readonly stdin: number | ReadableStream | FileSink | undefined;
readonly stdout: number | ReadableStream | undefined;
readonly stderr: number | ReadableStream | undefined;
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<Stdout, Stderr> {
readonly pid: number;
readonly success: boolean;
// the exact buffer types here are derived from the generic parameters
readonly stdout: Buffer | undefined;
readonly stderr: Buffer | undefined;
}
type ReadableSubprocess = Subprocess<any, "pipe", "pipe">;
type WritableSubprocess = Subprocess<"pipe", any, any>;
type PipedSubprocess = Subprocess<"pipe", "pipe", "pipe">;
type NullSubprocess = Subprocess<null, null, null>;
type ReadableSyncSubprocess = SyncSubprocess<"pipe", "pipe">;
type NullSyncSubprocess = SyncSubprocess<null, null>;
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";