Skip to content

Erstellen eines Prozesses (Bun.spawn())

Geben Sie einen Befehl als Array von Strings an. Das Ergebnis von Bun.spawn() ist ein Bun.Subprocess-Objekt.

ts
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0

Das zweite Argument für Bun.spawn ist ein Parameterobjekt, das zur Konfiguration des untergeordneten Prozesses verwendet werden kann.

ts
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 Prozesses

Eingabestream

Standardmäßig ist der Eingabestream des untergeordneten Prozesses nicht definiert; er kann mit dem stdin-Parameter konfiguriert werden.

ts
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); ..."
WertBeschreibung
nullStandard. 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 | DataViewEinen binären Puffer als Eingabe verwenden
ResponseDen body der Response als Eingabe verwenden
RequestDen body der Request als Eingabe verwenden
ReadableStreamEinen lesbaren Stream als Eingabe verwenden
BlobEin Blob als Eingabe verwenden
numberAus 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.

ts
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:

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

ts
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:

WertBeschreibung
"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
numberIn 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.

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

ts
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:

ts
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true

proc.kill(15); // Signalcode angeben
proc.kill("SIGTERM"); // Signalname angeben

Der ü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.

ts
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();

Ressourcennutzung

Sie können Informationen zur Ressourcennutzung des Prozesses erhalten, nachdem er beendet wurde:

ts
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:

ts
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:

ts
// 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öst

Standardmäßig werden Prozesse mit Timeout mit dem SIGTERM-Signal beendet. Sie können mit der killSignal-Option ein anderes Signal angeben:

ts
// 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:

ts
// '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 beendet

Interprozesskommunikation (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.

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

ts
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 senden

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

ts
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);
});
ts
// 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 JSC serialize-API serialisiert, die das Klonen von allem, was structuredClone unterstützt unterstützt. Dies unterstützt nicht das Übertragen von Objekteigentum.
  • json: Nachrichten werden mit JSON.stringify und JSON.parse serialisiert, was nicht so viele Objekttypen unterstützt wie advanced.

Um den IPC-Kanal vom übergeordneten Prozess zu trennen, rufen Sie auf:

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

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

  1. Es enthält eine success-Eigenschaft, die angibt, ob der Prozess mit einem Null-Exit-Code beendet wurde.
  2. Die stdout- und stderr-Eigenschaften sind Instanzen von Buffer anstelle von ReadableStream.
  3. Es gibt keine stdin-Eigenschaft. Verwenden Sie Bun.spawn, um inkrementell in den Eingabestream des untergeordneten Prozesses zu schreiben.
ts
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.

bash
bun spawn.mjs
txt
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
sh
node spawn.node.mjs
txt
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

Referenz

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.

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 | 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";

Bun von www.bunjs.com.cn bearbeitet