创建一个进程 (Bun.spawn())
提供一个字符串数组作为命令。Bun.spawn() 返回一个 Bun.Subprocess 对象。
const proc = Bun. spawn ([ " bun " , " --version " ]);
console. log ( await proc.exited); // 0
Bun.spawn 的第二个参数是一个参数对象,用来配置子进程。
const proc = Bun. spawn ([ " bun " , " --version " ], {
cwd : " ./path/to/subdir " , // 指定工作目录
env : { ... process.env, FOO : " bar " }, // 指定环境变量
onExit ( proc , exitCode , signalCode , error ) {
// 退出处理器
},
});
proc.pid; // 子进程的进程ID
输入流
默认情况下,子进程的输入流是 undefined;可以通过 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); ..."
值 描述 null默认。 不向子进程提供输入"pipe"返回一个 FileSink 用于快速增量写入 "inherit"继承父进程的 stdin Bun.file()从指定文件读取 TypedArray | DataView使用二进制缓冲区作为输入 Response使用响应体 body 作为输入 Request使用请求体 body 作为输入 ReadableStream使用可读流作为输入 Blob使用 Blob 作为输入 number从给定文件描述符的文件读取
"pipe" 选项允许从父进程向子进程的输入流进行增量写入。
const proc = Bun. spawn ([ " cat " ], {
stdin : " pipe " , // 返回可写的 FileSink
});
// 入队字符串数据
proc.stdin. write ( " hello " );
// 入队二进制数据
const enc = new TextEncoder ();
proc.stdin. write (enc. encode ( " world! " ));
// 发送缓冲数据
proc.stdin. flush ();
// 关闭输入流
proc.stdin. end ();
将一个 ReadableStream 传给 stdin,可以让你直接从 JavaScript 的 ReadableStream 向子进程输入流传输数据:
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!"
输出流
你可以通过 stdout 和 stderr 属性读取子进程的输出。默认情况下它们是 ReadableStream 的实例。
const proc = Bun. spawn ([ " bun " , " --version " ]);
const text = await proc.stdout. text ();
console. log (text); // => "1.3.3\n"
通过向 stdout/stderr 传入以下值之一来配置输出流:
值 描述 "pipe"stdout 的默认值。 将输出导入返回的 Subprocess 对象的 ReadableStream"inherit"stderr 的默认值。 继承自父进程"ignore"丢弃输出 Bun.file()写入指定文件 number写入指定文件描述符的文件
退出处理
使用 onExit 回调监听进程退出或被杀死。
index.ts
const proc = Bun. spawn ([ " bun " , " --version " ], {
onExit ( proc , exitCode , signalCode , error ) {
// 退出处理器
},
});
为方便起见,exited 属性是一个在进程退出时解析的 Promise。
index.ts
const proc = Bun. spawn ([ " bun " , " --version " ]);
await proc.exited; // 进程退出时解析
proc.killed; // 布尔值 — 进程是否被杀死?
proc.exitCode; // null | 数字
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
杀死进程:
index.ts
const proc = Bun. spawn ([ " bun " , " --version " ]);
proc. kill ();
proc.killed; // true
proc. kill ( 15 ); // 指定信号码
proc. kill ( " SIGTERM " ); // 指定信号名
父进程 bun 在所有子进程退出前不会终止。使用 proc.unref() 可以将子进程与父进程分离。
index.ts
const proc = Bun. spawn ([ " bun " , " --version " ]);
proc. unref ();
资源使用
进程退出后,你可以获取该进程的资源使用信息:
index.ts
const proc = Bun. spawn ([ " bun " , " --version " ]);
await proc.exited;
const usage = proc. resourceUsage ();
console. log ( `最大内存使用量: ${ usage . maxRSS } 字节` );
console. log ( `CPU 时间(用户态): ${ usage . cpuTime . user } 微秒` );
console. log ( `CPU 时间(内核态): ${ usage . cpuTime . system } 微秒` );
使用 AbortSignal
你可以使用 AbortSignal 中止子进程:
index.ts
const controller = new AbortController ();
const { signal } = controller;
const proc = Bun. spawn ({
cmd : [ " sleep " , " 100 " ],
signal,
});
// 后续中止进程:
controller. abort ();
使用 timeout 和 killSignal
你可以为子进程设置超时时间,超时后自动终止进程:
index.ts
// 5秒后杀死进程
const proc = Bun. spawn ({
cmd : [ " sleep " , " 10 " ],
timeout : 5000 , // 5秒,单位毫秒
});
await proc.exited; // 5秒后解析
默认情况下,超时进程会使用 SIGTERM 信号被杀死。你可以通过 killSignal 选项指定不同信号:
index.ts
// 5秒后使用 SIGKILL 杀死进程
const proc = Bun. spawn ({
cmd : [ " sleep " , " 10 " ],
timeout : 5000 ,
killSignal : " SIGKILL " , // 可为信号名称或信号编号
});
killSignal 选项也控制在 AbortSignal 触发时发送的信号。
使用 maxBuffer
对于 spawnSync,你可以限制输出的最大字节数,超过则杀死进程:
index.ts
// ‘yes’ 进程输出超过100字节后被杀死
const result = Bun. spawnSync ({
cmd : [ " yes " ], // Windows 下使用 ["bun", "exec", "yes"]
maxBuffer : 100 ,
});
// 进程退出
进程间通信 (IPC)
Bun 支持在两个 bun 进程间建立直接的进程间通信通道。要从被创建的 Bun 子进程接收消息,请指定 ipc 处理器。
parent.ts
const child = Bun. spawn ([ " bun " , " child.ts " ], {
ipc ( message ) {
/**
* 从子进程收到的消息
**/
},
});
父进程可通过返回的 Subprocess 对象上的 .send() 方法向子进程发送消息。子进程在 ipc 处理器第二个参数中获得发送者引用。
parent.ts
const childProc = Bun. spawn ([ " bun " , " child.ts " ], {
ipc ( message , childProc ) {
/**
* 从子进程收到的消息
**/
childProc. send ( " Respond to child " );
},
});
childProc. send ( " I am your father " ); // 父进程也可向子进程发送消息
子进程可用 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 => {
// 打印来自父进程的消息
console. log (message);
});
// 发送字符串消息
process. send ( " Hello from child as string " );
// 发送对象消息
process. send ({ message : " Hello from child as object " });
serialization 选项控制两进程间通信的序列化格式:
advanced:(默认)消息使用 JSC 的 serialize API 序列化,支持克隆所有 structuredClone 支持的类型 ,但不支持对象所有权转移。
json:消息使用 JSON.stringify 和 JSON.parse 序列化,支持对象类型比 advanced 少。
断开与父进程的 IPC 通道可调用:
Bun 与 Node.js 之间的 IPC
要在 bun 和 Node.js 进程间使用 IPC,请在 Bun.spawn 设置 serialization: "json"。这是因为 Node.js 和 Bun 使用不同的 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` });
});
}
终端(PTY)支持
对于交互式终端应用,可以使用 terminal 选项为子进程附加伪终端(PTY)。这让子进程认为自己运行在真实终端中,启用彩色输出、光标移动和交互式提示等功能。
const proc = Bun. spawn ([ " bash " ], {
terminal : {
cols : 80 ,
rows : 24 ,
data ( terminal , data ) {
// 终端接收数据时被调用
process.stdout. write (data);
},
},
});
// 向终端写数据
proc.terminal. write ( " echo hello \n " );
// 等待进程退出
await proc.exited;
// 关闭终端
proc.terminal. close ();
提供 terminal 选项时:
子进程中 process.stdout.isTTY 为 true
stdin、stdout 和 stderr 都绑定到终端
proc.stdin、proc.stdout 和 proc.stderr 返回 null —— 使用终端对象代替
可通过 proc.terminal 访问终端
终端选项
选项 描述 默认值 cols列数 80rows行数 24namePTY 配置的终端类型(通过 env 选项单独设置 TERM 环境变量) "xterm-256color"data接收数据时回调 (terminal, data) => void — exitPTY 流关闭时回调(EOF 或错误)。exitCode 是 PTY 生命周期状态(0为EOF,1为错误),而非子进程退出码。使用 proc.exited 监听进程退出。 — drain可写流准备好接收更多数据时回调 (terminal) => void —
终端方法
proc.terminal 返回的 Terminal 对象具有以下方法:
// 向终端写入数据
proc.terminal. write ( " echo hello \n " );
// 调整终端大小
proc.terminal. resize ( 120 , 40 );
// 设置原生模式(禁用行缓冲与回显)
proc.terminal. setRawMode ( true );
// 保持事件循环活动直到终端关闭
proc.terminal. ref ();
proc.terminal. unref ();
// 关闭终端
proc.terminal. close ();
可复用终端
你可以独立创建一个终端对象并在多个子进程间重用:
await using terminal = new Bun. Terminal ({
cols : 80 ,
rows : 24 ,
data ( term , data ) {
process.stdout. write (data);
},
});
// 创建第一个进程
const proc1 = Bun. spawn ([ " echo " , " first " ], { terminal });
await proc1.exited;
// 复用终端启动另一个进程
const proc2 = Bun. spawn ([ " echo " , " second " ], { terminal });
await proc2.exited;
// 终端会被 `await using` 自动关闭
传入已有的 Terminal 对象时:
终端可跨多个进程复用
你负责何时关闭终端
exit 回调在调用 terminal.close() 时触发,而非每个子进程退出时
使用 proc.exited 侦测每个子进程退出
这适合在同一终端会话中顺序运行多个命令。
终端支持仅限 POSIX 系统(Linux、macOS)。Windows 不支持此功能。
阻塞式 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 适合 HTTP 服务器和应用,Bun.spawnSync 更适合构建命令行工具。
性能基准
Bun 的 spawnSync 比 Node.js 的 child_process 模块快 60%。
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
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 中的定义。
See Typescript Definitions
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 ;
terminal ?: TerminalOptions ; // 仅限 POSIX 的 PTY 支持
}
type Readable =
| " pipe "
| " inherit "
| " ignore "
| null // 等价于 "ignore"
| undefined // 使用默认值
| BunFile
| ArrayBufferView
| number ;
type Writable =
| " pipe "
| " inherit "
| " ignore "
| null // 等价于 "ignore"
| undefined // 使用默认值
| BunFile
| ArrayBufferView
| number
| ReadableStream
| Blob
| Response
| Request ;
}
interface Subprocess extends AsyncDisposable {
readonly stdin : FileSink | number | undefined | null ;
readonly stdout : ReadableStream < Uint8Array < ArrayBuffer >> | number | undefined | null ;
readonly stderr : ReadableStream < Uint8Array < ArrayBuffer >> | number | undefined | null ;
readonly readable : ReadableStream < Uint8Array < ArrayBuffer >> | number | undefined | null ;
readonly terminal : Terminal | 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 TerminalOptions {
cols ?: number ;
rows ?: number ;
name ?: string ;
data ?: ( terminal : Terminal , data : Uint8Array < ArrayBuffer >) => void ;
/** PTY 流关闭时调用(EOF 或错误)。exitCode 是 PTY 生命周期状态(0=EOF,1=错误),非子进程退出码。 */
exit ?: ( terminal : Terminal , exitCode : number , signal : string | null ) => void ;
drain ?: ( terminal : Terminal ) => void ;
}
interface Terminal extends AsyncDisposable {
readonly stdin : number ;
readonly stdout : number ;
readonly closed : boolean ;
write ( data : string | BufferSource ) : number ;
resize ( cols : number , rows : number ) : void ;
setRawMode ( enabled : boolean ) : void ;
ref () : void ;
unref () : void ;
close () : void ;
}
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 " ;
See all 177 lines