Skip to main content

创建一个进程 (Bun.spawn())

提供一个字符串数组作为命令。Bun.spawn() 返回一个 Bun.Subprocess 对象。
Bun.spawn 的第二个参数是一个用于配置子进程的参数对象。

输入流

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

输出流

stdoutstderr 属性中读取子进程的输出。默认情况下,这些属性是 ReadableStream 的实例。
通过向 stdout/stderr 传入以下值之一来配置输出流:

退出处理

使用 onExit 回调监听进程退出或被杀死。
index.ts
exited 属性是一个 Promise,在进程退出时解析。
index.ts
杀死进程:
index.ts
bun 进程不会终止,直到所有子进程都已退出。使用 proc.unref() 将子进程与父进程分离。
index.ts

资源使用

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

使用 cgroups 限制资源(Linux)

在 Linux 上,传入 cgroup 可在控制组内启动子进程。子进程在开始执行前就会加入 cgroup,因此配置在其中的限制——内存、pids、CPU——会从第一条指令开始生效,并应用于该子进程随后生成的每个进程。当超过内存限制时,内核会在 cgroup 内部 OOM-kill 一个进程,而不是从父进程回收内存。 cgroup 是 /sys/fs/cgroup 下的一个目录;使用普通文件操作创建并配置它,然后传入其路径(或已打开的目录文件描述符):
index.ts
同一个目录可以传递给任意数量的 spawn;限制适用于它们的总使用量。同时支持 cgroup v1 和 v2 层级结构。创建 cgroup 通常需要 root 权限或一个已委派的子树。在其他平台上,此选项会被忽略;在 Linux 上,如果无法加入 cgroup,spawn 会失败。

使用 AbortSignal

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

使用 timeout 和 killSignal

timeout 设置为在经过指定的毫秒数后终止子进程:
index.ts
默认情况下,Bun 会使用 SIGTERM 终止超时的进程。使用 killSignal 选项指定其他信号:
index.ts
killSignal 选项也控制在 AbortSignal 触发时发送的信号。

使用 maxBuffer

对于 Bun.spawnSyncmaxBuffer 限制进程在 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.stringifyJSON.parse 序列化,支持对象类型比 advanced 少。
断开与父进程的 IPC 通道可调用:

Bun 与 Node.js 之间的 IPC

要在 bun 和 Node.js 进程间使用 IPC,请在 Bun.spawn 中设置 serialization: "json"。这是因为 Node.js 和 Bun 使用不同的 JS 引擎及不同的对象序列化格式。
bun-node-ipc.js

终端(PTY)支持

对于交互式终端应用,使用 terminal 选项启动附加伪终端(PTY)的子进程。子进程会看到一个真实终端,从而支持彩色输出、光标移动和交互式提示。
提供 terminal 选项时:
  • 子进程中 process.stdout.isTTYtrue
  • stdinstdoutstderr 都绑定到终端
  • proc.stdinproc.stdoutproc.stderr 返回 null —— 使用终端对象代替
  • 可通过 proc.terminal 访问终端

终端选项

终端方法

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

可复用终端

要通过同一个终端会话按顺序运行多个命令,可以独立创建终端,并在多个子进程之间复用:
传入已有的 Terminal 对象时:
  • 终端可跨多个进程复用
  • 你负责何时关闭终端
  • exit 回调在调用 terminal.close() 时触发,而非每个子进程退出时
  • 使用 proc.exited 侦测每个子进程退出

平台差异

Bun.Terminal 在 Linux 和 macOS 上使用 openpty(),在 Windows 上使用 ConPTY(CreatePseudoConsole)。核心行为在每个平台上都相同:子进程看到的是 TTY,write() 的内容会到达子进程的 stdin,子进程的输出会到达 data 回调,resize() 会更新子进程所看到的终端大小。但有一些细节存在差异:
  • Windows 上没有 termios。 inputFlagsoutputFlagslocalFlagscontrolFlags 始终读取为 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。调用 resize() 后,process.stdout.columns/rows 仍会更新。这是 libuv 的限制,会影响任何基于 libuv 的子进程(包括 Node.js)。
  • 在 Windows 11 24H2(版本 26100)之前的系统上,terminal.close() 可能无法及时终止仍在运行的子进程,因为在这些版本中,ClosePseudoConsole 会阻塞,直到 conhost 通过管道刷新完其输出。如果需要在子进程仍在运行时拆除终端,请先终止附加的进程。

阻塞式 API (Bun.spawnSync())

Bun.spawnSyncBun.spawn 的阻塞式等价 API。它支持相同的输入和参数,并返回一个 SyncSubprocess 对象,该对象在几个方面不同于 Subprocess
  1. 含有表示进程是否以零退出码退出的 success 属性。
  2. stdoutstderrBuffer 实例,而非 ReadableStream
  3. 没有 stdin 属性。若需增量写入输入流,请使用 Bun.spawn
原则上,异步的 Bun.spawn 适合 HTTP 服务器和应用,Bun.spawnSync 更适合构建命令行工具。

性能基准

⚡️ Bun.spawnBun.spawnSync 使用 posix_spawn(3)
Bun 的 spawnSync 比 Node.js 的 child_process 模块快 60%。
terminal
terminal

参考

以下是 Spawn API 和类型的参考。实际类型包含复杂的泛型,用于根据传递给 Bun.spawnBun.spawnSync 的选项,对 Subprocess 流进行强类型约束。完整详情请参阅 bun.d.ts
查看可展开的 TypeScript 定义