Skip to main content

创建进程(Bun.spawn())

以字符串数组的形式提供命令。Bun.spawn() 的结果是一个 Bun.Subprocess 对象。
Bun.spawn 的第二个参数是一个配置子进程的参数对象。

输入流

默认情况下,子进程的输入流是未定义的;通过 stdin 参数进行配置。
使用 "pipe",父进程可以增量写入子进程的输入流。
将 ReadableStream 传递给 stdin 会将其数据直接管道传输到子进程的输入:

输出流

从 stdout 和 stderr 属性读取子进程的输出。默认情况下,它们是 ReadableStream 的实例。
通过向 stdout/stderr 传递以下值之一来配置输出流:

退出处理

使用 onExit 回调监听进程退出或被杀死的事件。
index.ts
exited 属性是一个 Promise,在进程退出时 resolve。
index.ts
要杀死一个进程:
index.ts
父 bun 进程在所有子进程退出后才会终止。使用 proc.unref() 将子进程与父进程分离。
index.ts

资源使用

进程退出后,resourceUsage() 会报告其资源使用情况:
index.ts

使用 AbortSignal

您可以使用 AbortSignal 中止子进程:
index.ts

使用 timeout 和 killSignal

设置 timeout 可以在指定毫秒数后终止子进程:
index.ts
默认情况下,Bun 使用 SIGTERM 杀死超时进程。使用 killSignal 选项指定不同的信号:
index.ts
killSignal 选项还控制在 AbortSignal 被中止时发送的信号。

使用 maxBuffer

对于 Bun.spawnSync,maxBuffer 限制进程在 Bun 杀死它之前可以发出的输出字节数:
index.ts
Bun 在超过限制后立即停止读取,因此返回的输出只会超过 maxBuffer 一个读取的量,而不会超过进程在杀死信号到达之前设法写入的量。这与 Node.js 的行为一致。

进程间通信(IPC)

Bun 支持两个 bun 进程之间的直接进程间通信通道。要从生成的 Bun 子进程接收消息,请指定一个 ipc 处理器。
parent.ts
父进程通过返回的 Subprocess 实例上的 .send() 方法向子进程发送消息。ipc 处理器也会接收发送消息的子进程作为其第二个参数。
parent.ts
子进程使用 process.send() 向父进程发送消息,并使用 process.on("message") 接收消息。这与 Node.js 中 child_process.fork() 使用的 API 相同。
child.ts
child.ts
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 使用不同的 JavaScript 引擎和不同的对象序列化格式。
bun-node-ipc.js

终端(PTY)支持

对于交互式终端应用程序,请使用 terminal 选项来生成附加了伪终端(PTY)的子进程。子进程会看到一个真正的终端,从而支持彩色输出、光标移动和交互式提示。
当提供 terminal 选项时:
  • 子进程看到 process.stdout.isTTY 为 true
  • stdin、stdout 和 stderr 都连接到终端
  • proc.stdin、proc.stdout 和 proc.stderr 返回 null — 请改用终端
  • 通过 proc.terminal 访问终端

终端选项

终端方法

proc.terminal 返回的 Terminal 对象有以下方法:

可复用终端

要在同一个终端会话中顺序运行多个命令,可以独立创建一个终端并在多个子进程之间复用:
当传递现有的 Terminal 对象时:
  • 终端可以在多次 spawn 中复用
  • 您可以控制何时关闭终端
  • exit 回调在调用 terminal.close() 时触发,而不是在每个子进程退出时
  • 使用 proc.exited 检测单个子进程的退出

平台差异

Bun.Terminal 在 Linux 和 macOS 上使用 openpty(),在 Windows 上使用 ConPTY(CreatePseudoConsole)。核心行为——子进程看到 TTY、write() 到达子进程的 stdin、子进程输出到达 data 回调、resize() 更新子进程的视图——在所有平台上都是相同的。一些细节有所不同:
  • Windows 上没有 termios。 inputFlags、outputFlags、localFlags 和 controlFlags 始终读取为 0,设置它们无效。setRawMode() 会记录该标志但对子进程没有效果;子进程控制自己的控制台模式。
  • Windows 上没有子进程时不会回显。 在 POSIX 上,即使没有附加进程,内核的行规程也会将 write() 输入回显到 data 回调。ConPTY 没有行规程;输入会被缓冲等待下一个读取者。如果需要回显,请启动一个会回显的进程。
  • ConPTY 会重新编码输出。 ConPTY 将子进程的输出渲染到虚拟屏幕,并发出描述结果的任何 VT 序列,因此 data 回调接收的转义序列在语义上等价,但字节不一定相同。颜色和文本被保留;光标定位和重置序列可能会被重新排序或合并。ConPTY 还会在任何子进程输出之前发出一个简短的 VT 初始化序列(\x1b[?9001h\x1b[?1004h…)。
  • 在 Windows 上,输入的 \r 不会转换为 \n。 POSIX ICRNL 会将回车符映射为输入中的换行符;ConPTY 原样传递 \r。
  • 在 ConPTY 下,子进程中的 process.on('SIGWINCH') 不会触发,除非子进程以原始模式读取 stdin。process.stdout.columns/rows 在 resize() 后确实会更新。这是 libuv 的限制,影响所有基于 libuv 的子进程(包括 Node.js)。
  • 在 Windows 11 24H2(内部版本 26100)之前的版本上,terminal.close() 可能不会及时终止仍在运行的子进程,因为在这些版本上 ClosePseudoConsole 会阻塞直到 conhost 将其输出通过管道刷新完毕。如果需要在子进程仍在运行时销毁,请先杀死附加的进程。

阻塞 API(Bun.spawnSync())

Bun.spawnSync 是 Bun.spawn 的阻塞等价物。它支持相同的输入和参数,并返回一个 SyncSubprocess 对象,该对象在几个方面与 Subprocess 不同。
  1. 它包含一个 success 属性,指示进程是否以零退出代码退出。
  2. stdout 和 stderr 属性是 Buffer 的实例,而不是 ReadableStream。
  3. 没有 stdin 属性。请使用 Bun.spawn 来增量写入子进程的输入流。
一般规则:异步的 Bun.spawn API 更适合 HTTP 服务器和应用程序,而 Bun.spawnSync 更适合构建命令行工具。

基准测试

⚡️ Bun.spawn 和 Bun.spawnSync 使用 posix_spawn(3)。
Bun 的 spawnSync 创建进程的速度比 Node.js child_process 模块快 60%。
terminal
terminal

参考

以下是 Spawn API 和类型的参考。实际类型具有复杂的泛型,可以根据传递给 Bun.spawn 和 Bun.spawnSync 的选项强类型化 Subprocess 流。有关完整细节,请参阅 bun.d.ts。
查看 TypeScript 定义 可展开