Erstellen eines Prozesses (Bun.spawn())
Geben Sie einen Befehl als Array von Strings an. Das Ergebnis von Bun.spawn() ist ein Bun.Subprocess-Objekt.
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0Das zweite Argument für Bun.spawn ist ein Parameterobjekt, das zur Konfiguration des untergeordneten Prozesses verwendet werden kann.
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // Arbeitsverzeichnis angeben
env: { ...process.env, FOO: "bar" }, // Umgebungsvariablen angeben
onExit(proc, exitCode, signalCode, error) {
// Exit-Handler
},
});
proc.pid; // Prozess-ID des untergeordneten ProzessesEingabestream
Standardmäßig ist der Eingabestream des untergeordneten Prozesses nicht definiert; er kann mit dem stdin-Parameter konfiguriert werden.
const proc = Bun.spawn(["cat"], {
stdin: await fetch("https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js"),
});
const text = await proc.stdout.text();
console.log(text); // "const input = "hello world".repeat(400); ..."| Wert | Beschreibung |
|---|---|
null | Standard. Keine Eingabe für den untergeordneten Prozess bereitstellen |
"pipe" | Gibt ein FileSink für schnelles inkrementelles Schreiben zurück |
"inherit" | stdin des übergeordneten Prozesses erben |
Bun.file() | Aus der angegebenen Datei lesen |
TypedArray | DataView | Einen binären Puffer als Eingabe verwenden |
Response | Den body der Response als Eingabe verwenden |
Request | Den body der Request als Eingabe verwenden |
ReadableStream | Einen lesbaren Stream als Eingabe verwenden |
Blob | Ein Blob als Eingabe verwenden |
number | Aus der Datei mit einem bestimmten Dateideskriptor lesen |
Die "pipe"-Option ermöglicht das inkrementelle Schreiben in den Eingabestream des untergeordneten Prozesses vom übergeordneten Prozess aus.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // gibt ein FileSink zum Schreiben zurück
});
// String-Daten in die Warteschlange stellen
proc.stdin.write("hello");
// Binärdaten in die Warteschlange stellen
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
// Gepufferte Daten senden
proc.stdin.flush();
// Eingabestream schließen
proc.stdin.end();Das Übergeben eines ReadableStream an stdin ermöglicht es, Daten von einem JavaScript ReadableStream direkt an die Eingabe des untergeordneten Prozesses zu leiten:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello from ");
controller.enqueue("ReadableStream!");
controller.close();
},
});
const proc = Bun.spawn(["cat"], {
stdin: stream,
stdout: "pipe",
});
const output = await proc.stdout.text();
console.log(output); // "Hello from ReadableStream!"Ausgabestreams
Sie können Ergebnisse vom untergeordneten Prozess über die Eigenschaften stdout und stderr lesen. Standardmäßig sind dies Instanzen von ReadableStream.
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"Konfigurieren Sie den Ausgabestream, indem Sie einen der folgenden Werte an stdout/stderr übergeben:
| Wert | Beschreibung |
|---|---|
"pipe" | Standard für stdout. Leitet die Ausgabe an einen ReadableStream auf dem zurückgegebenen Subprocess-Objekt weiter |
"inherit" | Standard für stderr. Vom übergeordneten Prozess erben |
"ignore" | Die Ausgabe verwerfen |
Bun.file() | In die angegebene Datei schreiben |
number | In die Datei mit dem angegebenen Dateideskriptor schreiben |
Exit-Behandlung
Verwenden Sie den onExit-Callback, um zu erkennen, wenn der Prozess beendet oder getötet wird.
const proc = Bun.spawn(["bun", "--version"], {
onExit(proc, exitCode, signalCode, error) {
// Exit-Handler
},
});Der Einfachheit halber ist die exited-Eigenschaft ein Promise, der aufgelöst wird, wenn der Prozess beendet wird.
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited; // wird aufgelöst, wenn der Prozess beendet wird
proc.killed; // boolean — wurde der Prozess getötet?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...So beenden Sie einen Prozess:
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true
proc.kill(15); // Signalcode angeben
proc.kill("SIGTERM"); // Signalname angebenDer übergeordnete bun-Prozess wird nicht beendet, bis alle untergeordneten Prozesse beendet wurden. Verwenden Sie proc.unref(), um den untergeordneten Prozess vom übergeordneten Prozess zu trennen.
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();Ressourcennutzung
Sie können Informationen zur Ressourcennutzung des Prozesses erhalten, nachdem er beendet wurde:
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Maximal verwendeter Speicher: ${usage.maxRSS} Bytes`);
console.log(`CPU-Zeit (Benutzer): ${usage.cpuTime.user} µs`);
console.log(`CPU-Zeit (System): ${usage.cpuTime.system} µs`);Verwenden von AbortSignal
Sie können einen untergeordneten Prozess mit einem AbortSignal abbrechen:
const controller = new AbortController();
const { signal } = controller;
const proc = Bun.spawn({
cmd: ["sleep", "100"],
signal,
});
// Um den Prozess später abzubrechen:
controller.abort();Verwenden von Timeout und killSignal
Sie können ein Timeout für einen untergeordneten Prozess festlegen, um ihn nach einer bestimmten Dauer automatisch zu beenden:
// Prozess nach 5 Sekunden beenden
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000, // 5 Sekunden in Millisekunden
});
await proc.exited; // Wird nach 5 Sekunden aufgelöstStandardmäßig werden Prozesse mit Timeout mit dem SIGTERM-Signal beendet. Sie können mit der killSignal-Option ein anderes Signal angeben:
// Prozess nach 5 Sekunden mit SIGKILL beenden
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000,
killSignal: "SIGKILL", // Kann Signalname oder Signalnummer sein
});Die killSignal-Option steuert auch, welches Signal gesendet wird, wenn ein AbortSignal abgebrochen wird.
Verwenden von maxBuffer
Für spawnSync können Sie die maximale Anzahl von Bytes der Ausgabe begrenzen, bevor der Prozess beendet wird:
// 'yes' beenden, nachdem es über 100 Bytes Ausgabe erzeugt hat
const result = Bun.spawnSync({
cmd: ["yes"], // oder ["bun", "exec", "yes"] unter Windows
maxBuffer: 100,
});
// Prozess wird beendetInterprozesskommunikation (IPC)
Bun unterstützt einen direkten Interprozesskommunikationskanal zwischen zwei bun-Prozessen. Um Nachrichten von einem erstellten Bun-Unterprozess zu empfangen, geben Sie einen ipc-Handler an.
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* Die vom Unterprozess empfangene Nachricht
**/
},
});Der übergeordnete Prozess kann Nachrichten an den untergeordneten Prozess senden, indem er die .send()-Methode auf der zurückgegebenen Subprocess-Instanz verwendet. Eine Referenz auf den sendenden Unterprozess ist auch als zweites Argument im ipc-Handler verfügbar.
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* Die vom Unterprozess empfangene Nachricht
**/
childProc.send("Antwort an Kind");
},
});
childProc.send("Ich bin dein Vater"); // Der Elternteil kann auch Nachrichten an das Kind sendenDer untergeordnete Prozess kann meanwhile Nachrichten an seinen übergeordneten Prozess mit process.send() senden und Nachrichten mit process.on("message") empfangen. Dies ist dieselbe API, die für child_process.fork() in Node.js verwendet wird.
process.send("Hallo vom Kind als String");
process.send({ message: "Hallo vom Kind als Objekt" });
process.on("message", message => {
// Nachricht vom Elternteil ausgeben
console.log(message);
});// einen String senden
process.send("Hallo vom Kind als String");
// ein Objekt senden
process.send({ message: "Hallo vom Kind als Objekt" });Die serialization-Option steuert das zugrunde liegende Kommunikationsformat zwischen den beiden Prozessen:
advanced: (Standard) Nachrichten werden mit der JSCserialize-API serialisiert, die das Klonen von allem, wasstructuredCloneunterstützt unterstützt. Dies unterstützt nicht das Übertragen von Objekteigentum.json: Nachrichten werden mitJSON.stringifyundJSON.parseserialisiert, was nicht so viele Objekttypen unterstützt wieadvanced.
Um den IPC-Kanal vom übergeordneten Prozess zu trennen, rufen Sie auf:
childProc.disconnect();IPC zwischen Bun & Node.js
Um IPC zwischen einem bun-Prozess und einem Node.js-Prozess zu verwenden, setzen Sie serialization: "json" in Bun.spawn. Dies liegt daran, dass Node.js und Bun unterschiedliche JavaScript-Engines mit unterschiedlichen Objektserialisierungsformaten verwenden.
if (typeof Bun !== "undefined") {
const prefix = `[bun ${process.versions.bun} 🐇]`;
const node = Bun.spawn({
cmd: ["node", __filename],
ipc({ message }) {
console.log(message);
node.send({ message: `${prefix} 👋 hey node` });
node.kill();
},
stdio: ["inherit", "inherit", "inherit"],
serialization: "json",
});
node.send({ message: `${prefix} 👋 hey node` });
} else {
const prefix = `[node ${process.version}]`;
process.on("message", ({ message }) => {
console.log(message);
process.send({ message: `${prefix} 👋 hey bun` });
});
}Blockierende API (Bun.spawnSync())
Bun bietet ein synchrones Äquivalent zu Bun.spawn namens Bun.spawnSync. Dies ist eine blockierende API, die dieselben Eingaben und Parameter wie Bun.spawn unterstützt. Sie gibt ein SyncSubprocess-Objekt zurück, das sich in einigen Punkten von Subprocess unterscheidet.
- Es enthält eine
success-Eigenschaft, die angibt, ob der Prozess mit einem Null-Exit-Code beendet wurde. - Die
stdout- undstderr-Eigenschaften sind Instanzen vonBufferanstelle vonReadableStream. - Es gibt keine
stdin-Eigenschaft. Verwenden SieBun.spawn, um inkrementell in den Eingabestream des untergeordneten Prozesses zu schreiben.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"Als Faustregel gilt: Die asynchrone Bun.spawn-API ist besser für HTTP-Server und Apps, und Bun.spawnSync ist besser für die Erstellung von Befehlszeilentools.
Benchmarks
NOTE
⚡️ Unter der Haube verwenden `Bun.spawn` und `Bun.spawnSync` [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).Buns spawnSync erstellt Prozesse 60 % schneller als das Node.js child_process-Modul.
bun spawn.mjscpu: 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 msnode spawn.node.mjscpu: 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 msReferenz
Eine Referenz der Spawn-API und Typen wird unten gezeigt. Die echten Typen haben komplexe Generics, um die Subprocess-Streams stark mit den an Bun.spawn und Bun.spawnSync übergebenen Optionen zu typisieren. Für vollständige Details finden Sie diese Typen wie in bun.d.ts definiert.
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 | undefined>;
stdio?: [Writable, Readable, Readable];
stdin?: Writable;
stdout?: Readable;
stderr?: Readable;
onExit?(
subprocess: Subprocess,
exitCode: number | null,
signalCode: number | null,
error?: ErrorLike,
): void | Promise<void>;
ipc?(message: any, subprocess: Subprocess): void;
serialization?: "json" | "advanced";
windowsHide?: boolean;
windowsVerbatimArguments?: boolean;
argv0?: string;
signal?: AbortSignal;
timeout?: number;
killSignal?: string | number;
maxBuffer?: number;
}
type Readable =
| "pipe"
| "inherit"
| "ignore"
| null // entspricht "ignore"
| undefined // um Standard zu verwenden
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // entspricht "ignore"
| undefined // um Standard zu verwenden
| BunFile
| ArrayBufferView
| number
| ReadableStream
| Blob
| Response
| Request;
}
interface Subprocess extends AsyncDisposable {
readonly stdin: FileSink | number | undefined;
readonly stdout: ReadableStream<Uint8Array> | number | undefined;
readonly stderr: ReadableStream<Uint8Array> | number | undefined;
readonly readable: ReadableStream<Uint8Array> | number | undefined;
readonly pid: number;
readonly exited: Promise<number>;
readonly exitCode: number | null;
readonly signalCode: NodeJS.Signals | null;
readonly killed: boolean;
kill(exitCode?: number | NodeJS.Signals): void;
ref(): void;
unref(): void;
send(message: any): void;
disconnect(): void;
resourceUsage(): ResourceUsage | undefined;
}
interface SyncSubprocess {
stdout: Buffer | undefined;
stderr: Buffer | undefined;
exitCode: number;
success: boolean;
resourceUsage: ResourceUsage;
signalCode?: string;
exitedDueToTimeout?: true;
pid: number;
}
interface ResourceUsage {
contextSwitches: {
voluntary: number;
involuntary: number;
};
cpuTime: {
user: number;
system: number;
total: number;
};
maxRSS: number;
messages: {
sent: number;
received: number;
};
ops: {
in: number;
out: number;
};
shmSize: number;
signalCount: number;
swapCount: number;
}
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";