Spawn de um processo (Bun.spawn())
Forneça um comando como um array de strings. O resultado de Bun.spawn() é um objeto Bun.Subprocess.
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0O segundo argumento para Bun.spawn é um objeto de parâmetros que pode ser usado para configurar o subprocesso.
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // especifica um diretório de trabalho
env: { ...process.env, FOO: "bar" }, // especifica variáveis de ambiente
onExit(proc, exitCode, signalCode, error) {
// handler de saída
},
});
proc.pid; // ID do processo do subprocessoStream de entrada
Por padrão, o stream de entrada do subprocesso é undefined; pode ser configurado com o parâmetro stdin.
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); ..."| Valor | Descrição |
|---|---|
null | Padrão. Não fornece entrada para o subprocesso |
"pipe" | Retorna um FileSink para escrita incremental rápida |
"inherit" | Herda o stdin do processo pai |
Bun.file() | Lê do arquivo especificado |
TypedArray | DataView | Usa um buffer binário como entrada |
Response | Usa o body da response como entrada |
Request | Usa o body da request como entrada |
ReadableStream | Usa um readable stream como entrada |
Blob | Usa um blob como entrada |
number | Lê do arquivo com um dado file descriptor |
A opção "pipe" permite escrever incrementalmente no stream de entrada do subprocesso a partir do processo pai.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // retorna um FileSink para escrita
});
// enfileira dados de string
proc.stdin.write("hello");
// enfileira dados binários
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
// envia dados em buffer
proc.stdin.flush();
// fecha o stream de entrada
proc.stdin.end();Passar um ReadableStream para stdin permite canalizar dados de um ReadableStream JavaScript diretamente para a entrada do subprocesso:
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!"Streams de saída
Você pode ler resultados do subprocesso através das propriedades stdout e stderr. Por padrão, estas são instâncias de ReadableStream.
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"Configure o stream de saída passando um dos seguintes valores para stdout/stderr:
| Valor | Descrição |
|---|---|
"pipe" | Padrão para stdout. Canaliza a saída para um ReadableStream no objeto Subprocess retornado |
"inherit" | Padrão para stderr. Herda do processo pai |
"ignore" | Descarta a saída |
Bun.file() | Escreve no arquivo especificado |
number | Escreve no arquivo com o dado file descriptor |
Handler de saída
Use o callback onExit para ouvir quando o processo sair ou for encerrado.
const proc = Bun.spawn(["bun", "--version"], {
onExit(proc, exitCode, signalCode, error) {
// handler de saída
},
});Por conveniência, a propriedade exited é uma Promise que resolve quando o processo sai.
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited; // resolve quando o processo sai
proc.killed; // boolean — o processo foi encerrado?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...Para encerrar um processo:
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true
proc.kill(15); // especifica um código de sinal
proc.kill("SIGTERM"); // especifica um nome de sinalO processo pai bun não terminará até que todos os processos filhos tenham saído. Use proc.unref() para desanexar o processo filho do pai.
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();Uso de recursos
Você pode obter informações sobre o uso de recursos do processo após ele ter saído:
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Memória máxima usada: ${usage.maxRSS} bytes`);
console.log(`Tempo de CPU (user): ${usage.cpuTime.user} µs`);
console.log(`Tempo de CPU (system): ${usage.cpuTime.system} µs`);Usando AbortSignal
Você pode abortar um subprocesso usando um AbortSignal:
const controller = new AbortController();
const { signal } = controller;
const proc = Bun.spawn({
cmd: ["sleep", "100"],
signal,
});
// Depois, para abortar o processo:
controller.abort();Usando timeout e killSignal
Você pode definir um timeout para um subprocesso terminar automaticamente após uma duração específica:
// Encerra o processo após 5 segundos
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000, // 5 segundos em milissegundos
});
await proc.exited; // Será resolvido após 5 segundosPor padrão, processos com timeout são encerrados com o sinal SIGTERM. Você pode especificar um sinal diferente com a opção killSignal:
// Encerra o processo com SIGKILL após 5 segundos
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000,
killSignal: "SIGKILL", // Pode ser nome da string ou número do sinal
});A opção killSignal também controla qual sinal é enviado quando um AbortSignal é abortado.
Usando maxBuffer
Para spawnSync, você pode limitar o número máximo de bytes de saída antes que o processo seja encerrado:
// Encerra 'yes' após emitir mais de 100 bytes de saída
const result = Bun.spawnSync({
cmd: ["yes"], // ou ["bun", "exec", "yes"] no Windows
maxBuffer: 100,
});
// processo saiComunicação inter-processo (IPC)
O Bun suporta canal de comunicação inter-processo direto entre dois processos bun. Para receber mensagens de um subprocesso Bun spawnado, especifique um handler ipc.
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* A mensagem recebida do subprocesso
**/
},
});O processo pai pode enviar mensagens para o subprocesso usando o método .send() na instância Subprocess retornada. Uma referência ao subprocesso de envio também está disponível como o segundo argumento no handler ipc.
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* A mensagem recebida do subprocesso
**/
childProc.send("Responder ao filho");
},
});
childProc.send("Eu sou seu pai"); // O pai também pode enviar mensagens para o filhoEnquanto isso, o processo filho pode enviar mensagens para seu pai usando process.send() e receber mensagens com process.on("message"). Esta é a mesma API usada para child_process.fork() no Node.js.
process.send("Olá do filho como string");
process.send({ message: "Olá do filho como objeto" });
process.on("message", message => {
// imprime mensagem do pai
console.log(message);
});// envia uma string
process.send("Olá do filho como string");
// envia um objeto
process.send({ message: "Olá do filho como objeto" });A opção serialization controla o formato de comunicação subjacente entre os dois processos:
advanced: (padrão) Mensagens são serializadas usando a APIserializedo JSC, que suporta clonar tudo questructuredClonesuporta. Isso não suporta transferência de propriedade de objetos.json: Mensagens são serializadas usandoJSON.stringifyeJSON.parse, que não suporta tantos tipos de objetos quantoadvanced.
Para desconectar o canal IPC do processo pai, chame:
childProc.disconnect();IPC entre Bun e Node.js
Para usar IPC entre um processo bun e um processo Node.js, defina serialization: "json" em Bun.spawn. Isso ocorre porque Node.js e Bun usam motores JavaScript diferentes com formatos de serialização de objetos diferentes.
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} 👋 olá node` });
node.kill();
},
stdio: ["inherit", "inherit", "inherit"],
serialization: "json",
});
node.send({ message: `${prefix} 👋 olá node` });
} else {
const prefix = `[node ${process.version}]`;
process.on("message", ({ message }) => {
console.log(message);
process.send({ message: `${prefix} 👋 olá bun` });
});
}API Bloqueante (Bun.spawnSync())
O Bun fornece um equivalente síncrono de Bun.spawn chamado Bun.spawnSync. Esta é uma API bloqueante que suporta as mesmas entradas e parâmetros que Bun.spawn. Retorna um objeto SyncSubprocess, que difere de Subprocess de algumas formas.
- Contém uma propriedade
successque indica se o processo saiu com código de saída zero. - As propriedades
stdoutestderrsão instâncias deBufferem vez deReadableStream. - Não há propriedade
stdin. UseBun.spawnpara escrever incrementalmente no stream de entrada do subprocesso.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"Como regra geral, a API assíncrona Bun.spawn é melhor para servidores HTTP e apps, e Bun.spawnSync é melhor para construir ferramentas de linha de comando.
Benchmarks
NOTE
Internamente, `Bun.spawn` e `Bun.spawnSync` usam [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).O spawnSync do Bun spawniza processos 60% mais rápido que o módulo child_process do Node.js.
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 msReferência
Uma referência da Spawn API e tipos é mostrada abaixo. Os tipos reais têm generics complexos para tipar fortemente os streams Subprocess com as opções passadas para Bun.spawn e Bun.spawnSync. Para detalhes completos, encontre estes tipos conforme definidos em 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 | 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 // equivalente a "ignore"
| undefined // para usar padrão
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalente a "ignore"
| undefined // para usar padrão
| 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";