Skip to content

子进程

[稳定版: 2 - 稳定版]

稳定版: 2 稳定性: 2 - 稳定版

源代码: lib/child_process.js

node:child_process 模块提供了一种生成子进程的能力,其方式类似于,但并不完全相同于 popen(3)。此功能主要由 child_process.spawn() 函数提供:

js
const { spawn } = require('node:child_process')
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.stderr.on('data', data => {
  console.error(`stderr: ${data}`)
})

ls.on('close', code => {
  console.log(`child process exited with code ${code}`)
})
js
import { spawn } from 'node:child_process'
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.stderr.on('data', data => {
  console.error(`stderr: ${data}`)
})

ls.on('close', code => {
  console.log(`child process exited with code ${code}`)
})

默认情况下,stdinstdoutstderr 的管道在父 Node.js 进程和生成的子进程之间建立。这些管道的容量有限(并且是特定于平台的)。如果子进程写入 stdout 的数据超过该限制,而输出未被捕获,则子进程会阻塞,等待管道缓冲区接受更多数据。这与 shell 中管道的行为相同。如果不需要使用输出,请使用 { stdio: 'ignore' } 选项。

如果 options 对象中包含 env,则命令查找将使用 options.env.PATH 环境变量执行。否则,将使用 process.env.PATH。如果设置了 options.env 但没有 PATH,则在 Unix 上的查找将在默认搜索路径 /usr/bin:/bin 上执行(有关 execvpe/execvp,请参阅您的操作系统的手册),在 Windows 上,将使用当前进程的环境变量 PATH

在 Windows 上,环境变量不区分大小写。Node.js 会按字典顺序对 env 密钥进行排序,并使用第一个与之大小写不敏感匹配的密钥。只有第一个(按字典顺序)条目将传递给子进程。当将对象传递给具有多个相同密钥变体(例如 PATHPath)的 env 选项时,这可能会导致 Windows 上出现问题。

child_process.spawn() 方法异步生成子进程,不会阻塞 Node.js 事件循环。child_process.spawnSync() 函数以同步方式提供等效的功能,该功能会阻塞事件循环,直到生成的进程退出或被终止。

为方便起见,node:child_process 模块提供了一些同步和异步的替代方法,用于代替 child_process.spawn()child_process.spawnSync()。这些替代方法中的每一个都在 child_process.spawn()child_process.spawnSync() 之上实现。

对于某些用例(例如自动化 shell 脚本),同步对应项 可能更方便。但是,在许多情况下,同步方法会对性能产生重大影响,因为在生成的进程完成时会阻塞事件循环。

异步进程创建

child_process.spawn()child_process.fork()child_process.exec()child_process.execFile() 方法都遵循 Node.js API 的典型异步编程模式。

每个方法都返回一个 ChildProcess 实例。这些对象实现了 Node.js EventEmitter API,允许父进程注册监听函数,这些函数在子进程生命周期中的某些事件发生时被调用。

child_process.exec()child_process.execFile() 方法还可以指定一个可选的 callback 函数,该函数在子进程终止时被调用。

在 Windows 上运行 .bat.cmd 文件

child_process.exec()child_process.execFile() 之间的区别重要性取决于平台。在类 Unix 操作系统(Unix、Linux、macOS)上,child_process.execFile() 可能更高效,因为它默认不启动 shell。但是,在 Windows 上,.bat.cmd 文件本身无法执行,除非在终端中,因此无法使用 child_process.execFile() 启动。在 Windows 上运行时,可以使用 child_process.spawn() 并设置 shell 选项,使用 child_process.exec(),或者通过生成 cmd.exe 并将 .bat.cmd 文件作为参数传递来调用 .bat.cmd 文件(这就是 shell 选项和 child_process.exec() 所做的操作)。无论哪种情况,如果脚本文件名包含空格,都需要用引号括起来。

js
// OR...
const { exec, spawn } = require('node:child_process')

exec('my.bat', (err, stdout, stderr) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(stdout)
})

// 脚本文件名包含空格:
const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true })
// 或:
exec('"my script.cmd" a b', (err, stdout, stderr) => {
  // ...
})
js
// OR...
import { exec, spawn } from 'node:child_process'

exec('my.bat', (err, stdout, stderr) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(stdout)
})

// 脚本文件名包含空格:
const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true })
// 或:
exec('"my script.cmd" a b', (err, stdout, stderr) => {
  // ...
})

child_process.exec(command[, options][, callback])

[历史]

版本变更
v15.4.0添加了 AbortSignal 支持。
v16.4.0, v14.18.0cwd 选项可以使用 file: 协议的 WHATWG URL 对象。
v8.8.0现在支持 windowsHide 选项。
v0.1.90新增于:v0.1.90

生成一个 shell,然后在该 shell 中执行 command,缓冲任何生成的输出。传递给 exec 函数的 command 字符串由 shell 直接处理,特殊字符(根据 shell 不同而不同)需要相应处理:

js
const { exec } = require('node:child_process')

exec('"/path/to/test file/test.sh" arg1 arg2')
// 使用双引号,以便路径中的空格不被解释为多个参数的分隔符。

exec('echo "The \\$HOME variable is $HOME"')
// 第一个实例中转义了 $HOME 变量,第二个实例中没有转义。
js
import { exec } from 'node:child_process'

exec('"/path/to/test file/test.sh" arg1 arg2')
// 使用双引号,以便路径中的空格不被解释为多个参数的分隔符。

exec('echo "The \\$HOME variable is $HOME"')
// 第一个实例中转义了 $HOME 变量,第二个实例中没有转义。

切勿将未经消毒的用户输入传递给此函数。任何包含 shell 元字符的输入都可能用于触发任意命令执行。

如果提供了 callback 函数,则将使用参数 (error, stdout, stderr) 调用它。成功时,error 将为 null。出错时,error 将是 Error 的实例。error.code 属性将是进程的退出代码。按照惯例,任何非 0 的退出代码都表示错误。error.signal 将是终止进程的信号。

传递给回调的 stdoutstderr 参数将包含子进程的 stdout 和 stderr 输出。默认情况下,Node.js 将输出解码为 UTF-8 并将字符串传递给回调。encoding 选项可用于指定用于解码 stdout 和 stderr 输出的字符编码。如果 encoding'buffer' 或无法识别的字符编码,则将向回调传递 Buffer 对象。

js
const { exec } = require('node:child_process')
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`)
    return
  }
  console.log(`stdout: ${stdout}`)
  console.error(`stderr: ${stderr}`)
})
js
import { exec } from 'node:child_process'
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`)
    return
  }
  console.log(`stdout: ${stdout}`)
  console.error(`stderr: ${stderr}`)
})

如果 timeout 大于 0,则如果子进程运行时间超过 timeout 毫秒,父进程将发送由 killSignal 属性标识的信号(默认为 'SIGTERM')。

exec(3) POSIX 系统调用不同,child_process.exec() 不会替换现有进程,而是使用 shell 执行命令。

如果此方法作为其 util.promisify() 版本调用,则它将返回一个 Promise,该 Promise 用于包含 stdoutstderr 属性的 Object。返回的 ChildProcess 实例作为 child 属性附加到 Promise。如果发生错误(包括导致非 0 退出代码的任何错误),则返回一个被拒绝的 promise,其中包含回调中提供的相同 error 对象,但还包含两个附加属性 stdoutstderr

js
const util = require('node:util')
const exec = util.promisify(require('node:child_process').exec)

async function lsExample() {
  const { stdout, stderr } = await exec('ls')
  console.log('stdout:', stdout)
  console.error('stderr:', stderr)
}
lsExample()
js
import { promisify } from 'node:util'
import child_process from 'node:child_process'
const exec = promisify(child_process.exec)

async function lsExample() {
  const { stdout, stderr } = await exec('ls')
  console.log('stdout:', stdout)
  console.error('stderr:', stderr)
}
lsExample()

如果启用了 signal 选项,则在相应的 AbortController 上调用 .abort() 与在子进程上调用 .kill() 类似,不同之处在于传递给回调的错误将是 AbortError

js
const { exec } = require('node:child_process')
const controller = new AbortController()
const { signal } = controller
const child = exec('grep ssh', { signal }, error => {
  console.error(error) // 一个 AbortError
})
controller.abort()
js
import { exec } from 'node:child_process'
const controller = new AbortController()
const { signal } = controller
const child = exec('grep ssh', { signal }, error => {
  console.error(error) // 一个 AbortError
})
controller.abort()

child_process.execFile(file[, args][, options][, callback])

[历史]

版本变更
v16.4.0, v14.18.0cwd 选项可以使用使用 file: 协议的 WHATWG URL 对象。
v15.4.0, v14.17.0添加了 AbortSignal 支持。
v8.8.0现在支持 windowsHide 选项。
v0.1.91在 v0.1.91 中添加

child_process.execFile() 函数类似于 child_process.exec(),不同之处在于它默认不生成 shell。而是直接将指定的可执行文件 file 作为新进程生成,这使得它比 child_process.exec() 略微高效一些。

支持与 child_process.exec() 相同的选项。由于没有生成 shell,因此不支持 I/O 重定向和文件通配等行为。

js
const { execFile } = require('node:child_process')
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error
  }
  console.log(stdout)
})
js
import { execFile } from 'node:child_process'
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error
  }
  console.log(stdout)
})

传递给回调的 stdoutstderr 参数将包含子进程的 stdout 和 stderr 输出。默认情况下,Node.js 将以 UTF-8 解码输出并将字符串传递给回调。encoding 选项可用于指定用于解码 stdout 和 stderr 输出的字符编码。如果 encoding'buffer' 或无法识别的字符编码,则将 Buffer 对象传递给回调。

如果此方法被调用为其 util.promisify() 版本,它将返回一个 Promise,该 Promise 用于包含 stdoutstderr 属性的对象。返回的 ChildProcess 实例作为 child 属性附加到 Promise。如果发生错误(包括导致退出代码不为 0 的任何错误),则返回一个被拒绝的 promise,其中包含回调中提供的相同 error 对象,但包含两个附加属性 stdoutstderr

js
const util = require('node:util')
const execFile = util.promisify(require('node:child_process').execFile)
async function getVersion() {
  const { stdout } = await execFile('node', ['--version'])
  console.log(stdout)
}
getVersion()
js
import { promisify } from 'node:util'
import child_process from 'node:child_process'
const execFile = promisify(child_process.execFile)
async function getVersion() {
  const { stdout } = await execFile('node', ['--version'])
  console.log(stdout)
}
getVersion()

如果启用了shell选项,请勿将未经消毒的用户输入传递给此函数。任何包含 shell 元字符的输入都可能被用于触发任意命令执行。

如果启用了 signal 选项,则在相应的 AbortController 上调用 .abort() 与在子进程上调用 .kill() 类似,不同之处在于传递给回调的错误将是 AbortError

js
const { execFile } = require('node:child_process')
const controller = new AbortController()
const { signal } = controller
const child = execFile('node', ['--version'], { signal }, error => {
  console.error(error) // an AbortError
})
controller.abort()
js
import { execFile } from 'node:child_process'
const controller = new AbortController()
const { signal } = controller
const child = execFile('node', ['--version'], { signal }, error => {
  console.error(error) // an AbortError
})
controller.abort()

child_process.fork(modulePath[, args][, options])

[历史]

版本变更
v17.4.0, v16.14.0modulePath 参数可以使用使用 file: 协议的 WHATWG URL 对象。
v16.4.0, v14.18.0cwd 选项可以使用使用 file: 协议的 WHATWG URL 对象。
v15.13.0, v14.18.0添加了 timeout。
v15.11.0, v14.18.0为 AbortSignal 添加了 killSignal。
v15.6.0, v14.17.0添加了 AbortSignal 支持。
v13.2.0, v12.16.0现在支持 serialization 选项。
v8.0.0stdio 选项现在可以是字符串。
v6.4.0现在支持 stdio 选项。
v0.5.0在 v0.5.0 中添加
  • modulePath <字符串> | <URL> 在子进程中运行的模块。

  • args <字符串数组> 字符串参数列表。

  • options <对象>

    • cwd <字符串> | <URL> 子进程的当前工作目录。
    • detached <布尔值> 准备子进程独立于其父进程运行。具体行为取决于平台,请参见 options.detached)。
    • env <对象> 环境键值对。默认值: process.env
    • execPath <字符串> 用于创建子进程的可执行文件。
    • execArgv <字符串数组> 传递给可执行文件的字符串参数列表。默认值: process.execArgv
    • gid <数字> 设置进程的组标识 (参见 setgid(2))。
    • serialization <字符串> 指定用于在进程之间发送消息的序列化类型。可能的值为 'json''advanced'。有关更多详细信息,请参见高级序列化默认值: 'json'
    • signal <AbortSignal> 允许使用 AbortSignal 关闭子进程。
    • killSignal <字符串> | <整数> 当生成的进程因超时或中止信号而被终止时要使用的信号值。默认值: 'SIGTERM'
    • silent <布尔值> 如果为 true,则子进程的 stdin、stdout 和 stderr 将被管道传输到父进程,否则将从父进程继承它们,有关更多详细信息,请参见 child_process.spawn()stdio'pipe''inherit' 选项。默认值: false
    • stdio <数组> | <字符串> 请参见 child_process.spawn()stdio。提供此选项时,它会覆盖 silent。如果使用数组变体,则它必须包含正好一个值为 'ipc' 的项目,否则将抛出错误。例如 [0, 1, 2, 'ipc']
    • uid <数字> 设置进程的用户标识 (参见 setuid(2))。
    • windowsVerbatimArguments <布尔值> 在 Windows 上不进行参数的引用或转义。在 Unix 上忽略。默认值: false
    • timeout <数字> 以毫秒为单位,进程允许运行的最长时间。默认值: undefined
  • 返回值: <ChildProcess>

child_process.fork() 方法是 child_process.spawn() 的一个特例,专门用于生成新的 Node.js 进程。与 child_process.spawn() 一样,它返回一个 ChildProcess 对象。返回的 ChildProcess 将具有一个内置的附加通信通道,允许在父进程和子进程之间传递消息。有关详细信息,请参见 subprocess.send()

请记住,生成的 Node.js 子进程独立于父进程,但两者之间建立的 IPC 通信通道除外。每个进程都有其自己的内存,以及自己的 V8 实例。由于需要额外的资源分配,因此不建议生成大量 Node.js 子进程。

默认情况下,child_process.fork() 将使用父进程的 process.execPath 生成新的 Node.js 实例。options 对象中的 execPath 属性允许使用替代执行路径。

使用自定义 execPath 启动的 Node.js 进程将使用子进程上使用环境变量 NODE_CHANNEL_FD 标识的文件描述符 (fd) 与父进程进行通信。

fork(2) POSIX 系统调用不同,child_process.fork() 不会克隆当前进程。

child_process.spawn() 中可用的 shell 选项不受 child_process.fork() 支持,如果设置则会被忽略。

如果启用了 signal 选项,则在相应的 AbortController 上调用 .abort() 与在子进程上调用 .kill() 类似,不同之处在于传递给回调的错误将是 AbortError

js
const { fork } = require('node:child_process')
const process = require('node:process')

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`)
  }, 1_000)
} else {
  const controller = new AbortController()
  const { signal } = controller
  const child = fork(__filename, ['child'], { signal })
  child.on('error', err => {
    // 如果控制器中止,则将使用 err 为 AbortError 调用此函数
  })
  controller.abort() // 停止子进程
}
js
import { fork } from 'node:child_process'
import process from 'node:process'

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`)
  }, 1_000)
} else {
  const controller = new AbortController()
  const { signal } = controller
  const child = fork(import.meta.url, ['child'], { signal })
  child.on('error', err => {
    // 如果控制器中止,则将使用 err 为 AbortError 调用此函数
  })
  controller.abort() // 停止子进程
}

child_process.spawn(command[, args][, options])

[历史]

版本变更
v16.4.0, v14.18.0cwd 选项可以使用 file: 协议的 WHATWG URL 对象。
v15.13.0, v14.18.0添加了 timeout
v15.11.0, v14.18.0AbortSignal 添加了 killSignal
v15.5.0, v14.17.0添加了 AbortSignal 支持。
v13.2.0, v12.16.0现在支持 serialization 选项。
v8.8.0现在支持 windowsHide 选项。
v6.4.0现在支持 argv0 选项。
v5.7.0现在支持 shell 选项。
v0.1.90v0.1.90 版本中添加
  • command <字符串> 要运行的命令。

  • args <字符串数组> 字符串参数列表。

  • options <对象>

    • cwd <字符串> | <URL> 子进程的当前工作目录。
    • env <对象> 环境键值对。默认值: process.env
    • argv0 <字符串> 显式设置发送到子进程的 argv[0] 的值。如果未指定,则将其设置为 command
    • stdio <数组> | <字符串> 子进程的 stdio 配置(参见 options.stdio)。
    • detached <布尔值> 准备子进程独立于其父进程运行。具体行为取决于平台,请参见 options.detached)。
    • uid <数字> 设置进程的用户标识(参见 setuid(2))。
    • gid <数字> 设置进程的组标识(参见 setgid(2))。
    • serialization <字符串> 指定用于在进程之间发送消息的序列化类型。可能的值为 'json''advanced'。有关更多详细信息,请参见高级序列化默认值: 'json'
    • shell <布尔值> | <字符串> 如果为 true,则在 shell 内运行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以使用字符串指定不同的 shell。请参见Shell 要求默认 Windows shell默认值: false(无 shell)。
    • windowsVerbatimArguments <布尔值> 在 Windows 上不进行参数的引用或转义。在 Unix 上忽略。当指定 shell 并为 CMD 时,此选项会自动设置为 true默认值: false
    • windowsHide <布尔值> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。默认值: false
    • signal <AbortSignal> 允许使用 AbortSignal 中断子进程。
    • timeout <数字> 以毫秒为单位,进程允许运行的最长时间。默认值: undefined
    • killSignal <字符串> | <整数> 超时或中断信号终止子进程时使用的信号值。默认值: 'SIGTERM'
  • 返回值: <ChildProcess>

child_process.spawn() 方法使用给定的 command 生成一个新进程,命令行参数在 args 中。如果省略,args 默认为空数组。

如果启用了shell选项,请勿将未经消毒的用户输入传递给此函数。任何包含 shell 元字符的输入都可能被用来触发任意命令执行。

可以使用第三个参数来指定其他选项,其默认值如下:

js
const defaults = {
  cwd: undefined,
  env: process.env,
}

使用 cwd 指定生成进程的工作目录。如果未给出,则默认继承当前工作目录。如果给出,但路径不存在,则子进程发出 ENOENT 错误并立即退出。当命令不存在时,也会发出 ENOENT

使用 env 指定新进程可见的环境变量,默认为 process.env

env 中的 undefined 值将被忽略。

运行 ls -lh /usr、捕获 stdoutstderr 和退出代码的示例:

js
const { spawn } = require('node:child_process')
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.stderr.on('data', data => {
  console.error(`stderr: ${data}`)
})

ls.on('close', code => {
  console.log(`child process exited with code ${code}`)
})
js
import { spawn } from 'node:child_process'
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.stderr.on('data', data => {
  console.error(`stderr: ${data}`)
})

ls.on('close', code => {
  console.log(`child process exited with code ${code}`)
})

示例:运行 ps ax | grep ssh 的一种非常复杂的方法

js
const { spawn } = require('node:child_process')
const ps = spawn('ps', ['ax'])
const grep = spawn('grep', ['ssh'])

ps.stdout.on('data', data => {
  grep.stdin.write(data)
})

ps.stderr.on('data', data => {
  console.error(`ps stderr: ${data}`)
})

ps.on('close', code => {
  if (code !== 0) {
    console.log(`ps process exited with code ${code}`)
  }
  grep.stdin.end()
})

grep.stdout.on('data', data => {
  console.log(data.toString())
})

grep.stderr.on('data', data => {
  console.error(`grep stderr: ${data}`)
})

grep.on('close', code => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`)
  }
})
js
import { spawn } from 'node:child_process'
const ps = spawn('ps', ['ax'])
const grep = spawn('grep', ['ssh'])

ps.stdout.on('data', data => {
  grep.stdin.write(data)
})

ps.stderr.on('data', data => {
  console.error(`ps stderr: ${data}`)
})

ps.on('close', code => {
  if (code !== 0) {
    console.log(`ps process exited with code ${code}`)
  }
  grep.stdin.end()
})

grep.stdout.on('data', data => {
  console.log(data.toString())
})

grep.stderr.on('data', data => {
  console.error(`grep stderr: ${data}`)
})

grep.on('close', code => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`)
  }
})

检查 spawn 失败的示例:

js
const { spawn } = require('node:child_process')
const subprocess = spawn('bad_command')

subprocess.on('error', err => {
  console.error('Failed to start subprocess.')
})
js
import { spawn } from 'node:child_process'
const subprocess = spawn('bad_command')

subprocess.on('error', err => {
  console.error('Failed to start subprocess.')
})

某些平台(macOS、Linux)将使用 argv[0] 的值作为进程标题,而其他平台(Windows、SunOS)将使用 command

Node.js 在启动时用 process.execPath 覆盖 argv[0],因此 Node.js 子进程中的 process.argv[0] 将与从父进程传递到 spawnargv0 参数不匹配。请改用 process.argv0 属性来检索它。

如果启用了 signal 选项,则在相应的 AbortController 上调用 .abort() 与在子进程上调用 .kill() 类似,不同之处在于传递给回调的错误将是 AbortError

js
const { spawn } = require('node:child_process')
const controller = new AbortController()
const { signal } = controller
const grep = spawn('grep', ['ssh'], { signal })
grep.on('error', err => {
  // 如果控制器中止,则将使用 err 为 AbortError 来调用此函数
})
controller.abort() // 停止子进程
js
import { spawn } from 'node:child_process'
const controller = new AbortController()
const { signal } = controller
const grep = spawn('grep', ['ssh'], { signal })
grep.on('error', err => {
  // 如果控制器中止,则将使用 err 为 AbortError 来调用此函数
})
controller.abort() // 停止子进程

options.detached

新增于: v0.7.10

在 Windows 系统上,将 options.detached 设置为 true 使子进程能够在父进程退出后继续运行。子进程将拥有自己的控制台窗口。一旦为子进程启用,就无法禁用。

在非 Windows 平台上,如果将 options.detached 设置为 true,则子进程将成为新的进程组和会话的领导者。无论子进程是否分离,父进程退出后,子进程都可能继续运行。有关更多信息,请参阅 setsid(2)

默认情况下,父进程将等待分离的子进程退出。为防止父进程等待给定的 subprocess 退出,请使用 subprocess.unref() 方法。这样做将导致父进程的事件循环不将子进程包含在其引用计数中,从而允许父进程独立于子进程退出,除非子进程和父进程之间存在已建立的 IPC 通道。

当使用 detached 选项启动一个长期运行的进程时,除非该进程提供了一个未连接到父进程的 stdio 配置,否则该进程在父进程退出后不会在后台运行。如果继承了父进程的 stdio,则子进程将保持连接到控制终端。

一个长期运行进程的示例,通过分离并忽略其父进程的 stdio 文件描述符来忽略父进程的终止:

js
const { spawn } = require('node:child_process')
const process = require('node:process')

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()
js
import { spawn } from 'node:child_process'
import process from 'node:process'

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()

或者,可以将子进程的输出重定向到文件:

js
const { openSync } = require('node:fs')
const { spawn } = require('node:child_process')
const out = openSync('./out.log', 'a')
const err = openSync('./out.log', 'a')

const subprocess = spawn('prg', [], {
  detached: true,
  stdio: ['ignore', out, err],
})

subprocess.unref()
js
import { openSync } from 'node:fs'
import { spawn } from 'node:child_process'
const out = openSync('./out.log', 'a')
const err = openSync('./out.log', 'a')

const subprocess = spawn('prg', [], {
  detached: true,
  stdio: ['ignore', out, err],
})

subprocess.unref()

options.stdio

[历史]

版本变更
v15.6.0, v14.18.0添加了 overlapped stdio 标志。
v3.3.1现在接受 0 作为文件描述符。
v0.7.10在 v0.7.10 中添加

options.stdio 选项用于配置在父进程和子进程之间建立的管道。默认情况下,子进程的 stdin、stdout 和 stderr 会重定向到 ChildProcess 对象上的相应 subprocess.stdinsubprocess.stdoutsubprocess.stderr 流。这等同于将 options.stdio 设置为 ['pipe', 'pipe', 'pipe']

为方便起见,options.stdio 可以是以下字符串之一:

  • 'pipe':等同于 ['pipe', 'pipe', 'pipe'](默认值)
  • 'overlapped':等同于 ['overlapped', 'overlapped', 'overlapped']
  • 'ignore':等同于 ['ignore', 'ignore', 'ignore']
  • 'inherit':等同于 ['inherit', 'inherit', 'inherit'][0, 1, 2]

否则,options.stdio 的值是一个数组,其中每个索引对应于子进程中的一个 fd。fd 0、1 和 2 分别对应于 stdin、stdout 和 stderr。可以指定附加的 fd 以在父进程和子进程之间创建附加的管道。该值可以是以下之一:

js
const { spawn } = require('node:child_process')
const process = require('node:process')

// 子进程将使用父进程的标准输入输出。
spawn('prg', [], { stdio: 'inherit' })

// 生成仅共享 stderr 的子进程。
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] })

// 打开一个额外的 fd=4,与提供 startd 风格界面的程序交互。
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] })
js
import { spawn } from 'node:child_process'
import process from 'node:process'

// 子进程将使用父进程的标准输入输出。
spawn('prg', [], { stdio: 'inherit' })

// 生成仅共享 stderr 的子进程。
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] })

// 打开一个额外的 fd=4,与提供 startd 风格界面的程序交互。
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] })

*值得注意的是,当在父进程和子进程之间建立 IPC 通道,并且子进程是 Node.js 实例时,子进程将使用未引用的 IPC 通道启动(使用 unref()),直到子进程为 'disconnect' 事件或 'message' 事件注册事件处理程序。这允许子进程正常退出,而不会因打开的 IPC 通道而使进程保持打开状态。*另请参阅:child_process.exec()child_process.fork()

同步进程创建

child_process.spawnSync()child_process.execSync()child_process.execFileSync() 方法是同步的,它们会阻塞 Node.js 事件循环,暂停执行任何其他代码,直到生成的进程退出。

像这样的阻塞调用主要用于简化通用脚本任务以及简化启动时应用程序配置的加载/处理。

child_process.execFileSync(file[, args][, options])

[历史]

版本更改
v16.4.0, v14.18.0cwd 选项可以使用 file: 协议的 WHATWG URL 对象。
v10.10.0input 选项现在可以是任何 TypedArrayDataView
v8.8.0现在支持 windowsHide 选项。
v8.0.0input 选项现在可以是 Uint8Array
v6.2.1, v4.5.0encoding 选项现在可以显式设置为 buffer
v0.11.12新增于:v0.11.12
  • file <字符串> 要运行的可执行文件的名称或路径。

  • args <字符串数组> 字符串参数列表。

  • options <对象>

    • cwd <字符串> | <URL> 子进程的当前工作目录。
    • input <字符串> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传递给生成的进程的值。如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • stdio <字符串> | <数组> 子进程的 stdio 配置。参见 child_process.spawn()stdio。默认情况下,stderr 将输出到父进程的 stderr,除非指定了 stdio默认值:'pipe'
    • env <对象> 环境键值对。默认值:process.env
    • uid <数字> 设置进程的用户身份(参见 setuid(2))。
    • gid <数字> 设置进程的组身份(参见 setgid(2))。
    • timeout <数字> 进程允许运行的最长时间(以毫秒为单位)。默认值:undefined
    • killSignal <字符串> | <整数> 当生成的进程被杀死时要使用的信号值。默认值:'SIGTERM'
    • maxBuffer <数字> stdout 或 stderr 上允许的最大数据量(以字节为单位)。如果超过此限制,则子进程将被终止。参见maxBuffer 和 Unicode 中的警告。默认值:1024 * 1024
    • encoding <字符串> 用于所有 stdio 输入和输出的编码。默认值:'buffer'
    • windowsHide <布尔值> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。默认值:false
    • shell <布尔值> | <字符串> 如果为 true,则在 shell 内运行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以使用字符串指定不同的 shell。参见Shell 要求默认 Windows shell默认值:false(无 shell)。
  • 返回值: <Buffer> | <字符串> 命令的 stdout。

child_process.execFileSync() 方法通常与 child_process.execFile() 相同,区别在于该方法只有在子进程完全关闭后才会返回。当遇到超时并发送 killSignal 时,该方法只有在进程完全退出后才会返回。

如果子进程拦截并处理 SIGTERM 信号并且没有退出,父进程仍然会等到子进程退出。

如果进程超时或退出代码非零,此方法将抛出 Error ,其中包含底层 child_process.spawnSync() 的完整结果。

如果启用了shell选项,请勿将未经消毒的用户输入传递给此函数。任何包含 shell 元字符的输入都可能用于触发任意命令执行。

js
const { execFileSync } = require('node:child_process')

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // 捕获子进程的 stdout 和 stderr。覆盖将子 stderr 流式传输到父 stderr 的默认行为
    stdio: 'pipe',

    // 使用 utf8 编码进行 stdio 管道
    encoding: 'utf8',
  })

  console.log(stdout)
} catch (err) {
  if (err.code) {
    // 生成子进程失败
    console.error(err.code)
  } else {
    // 子进程已生成,但退出代码非零
    // 错误包含来自子进程的任何 stdout 和 stderr
    const { stdout, stderr } = err

    console.error({ stdout, stderr })
  }
}
js
import { execFileSync } from 'node:child_process'

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // 捕获子进程的 stdout 和 stderr。覆盖将子 stderr 流式传输到父 stderr 的默认行为
    stdio: 'pipe',

    // 使用 utf8 编码进行 stdio 管道
    encoding: 'utf8',
  })

  console.log(stdout)
} catch (err) {
  if (err.code) {
    // 生成子进程失败
    console.error(err.code)
  } else {
    // 子进程已生成,但退出代码非零
    // 错误包含来自子进程的任何 stdout 和 stderr
    const { stdout, stderr } = err

    console.error({ stdout, stderr })
  }
}

child_process.execSync(command[, options])

[历史]

版本变更
v16.4.0, v14.18.0cwd 选项可以使用 file: 协议的 WHATWG URL 对象。
v10.10.0input 选项现在可以是任何 TypedArrayDataView
v8.8.0现在支持 windowsHide 选项。
v8.0.0input 选项现在可以是 Uint8Array
v0.11.12新增于:v0.11.12
  • command <字符串> 要运行的命令。

  • options <对象>

    • cwd <字符串> | <URL> 子进程的当前工作目录。
    • input <字符串> | <Buffer> | <TypedArray> | <DataView> 将作为标准输入传递给生成的进程的值。如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • stdio <字符串> | <数组> 子进程的标准输入/输出配置。参见 child_process.spawn()stdio。默认情况下,stderr 将输出到父进程的 stderr,除非指定了 stdio默认值:'pipe'
    • env <对象> 环境键值对。默认值:process.env
    • shell <字符串> 用于执行命令的 Shell。参见 Shell 要求默认 Windows Shell。**默认值:**Unix 上为'/bin/sh',Windows 上为 process.env.ComSpec
    • uid <数字> 设置进程的用户身份。(参见 setuid(2))。
    • gid <数字> 设置进程的组身份。(参见 setgid(2))。
    • timeout <数字> 进程允许运行的最大时间(毫秒)。默认值:undefined
    • killSignal <字符串> | <整数> 当生成的进程被杀死时要使用的信号值。默认值:'SIGTERM'
    • maxBuffer <数字> stdout 或 stderr 上允许的最大数据量(字节)。如果超过此值,则子进程将被终止,任何输出都将被截断。参见 maxBuffer 和 Unicode 中的警告。默认值:1024 * 1024
    • encoding <字符串> 用于所有标准输入/输出的编码。默认值:'buffer'
    • windowsHide <布尔值> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。默认值:false
  • 返回值: <Buffer> | <字符串> 命令的标准输出。

child_process.execSync() 方法通常与 child_process.exec() 相同,区别在于此方法只有在子进程完全关闭后才会返回。当遇到超时并发送 killSignal 时,此方法只有在进程完全退出后才会返回。如果子进程拦截并处理 SIGTERM 信号并且没有退出,则父进程将等待直到子进程退出。

如果进程超时或退出代码非零,则此方法将抛出异常。Error 对象将包含 child_process.spawnSync() 的完整结果。

切勿将未经消毒的用户输入传递给此函数。任何包含 Shell 元字符的输入都可能被用来触发任意命令执行。

child_process.spawnSync(command[, args][, options])

[历史]

版本变更
v16.4.0, v14.18.0cwd 选项可以使用使用 file: 协议的 WHATWG URL 对象。
v10.10.0input 选项现在可以是任何 TypedArrayDataView
v8.8.0现在支持 windowsHide 选项。
v8.0.0input 选项现在可以是 Uint8Array
v5.7.0现在支持 shell 选项。
v6.2.1, v4.5.0encoding 选项现在可以显式设置为 buffer
v0.11.12新增于:v0.11.12
  • command <字符串> 要运行的命令。

  • args <字符串数组> 字符串参数列表。

  • options <对象>

    • cwd <字符串> | <URL> 子进程的当前工作目录。
    • input <字符串> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传递给生成的进程的值。如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • argv0 <字符串> 显式设置发送到子进程的 argv[0] 的值。如果未指定,则将其设置为 command
    • stdio <字符串> | <数组> 子进程的 stdio 配置。参见 child_process.spawn()stdio默认值: 'pipe'
    • env <对象> 环境键值对。默认值: process.env
    • uid <数字> 设置进程的用户身份(参见 setuid(2))。
    • gid <数字> 设置进程的组身份(参见 setgid(2))。
    • timeout <数字> 以毫秒为单位,进程允许运行的最大时间。默认值: undefined
    • killSignal <字符串> | <整数> 当要终止生成的进程时要使用的信号值。默认值: 'SIGTERM'
    • maxBuffer <数字> stdout 或 stderr 上允许的最大数据量(以字节为单位)。如果超过此值,则子进程将被终止,任何输出都将被截断。请参见 maxBuffer 和 Unicode 中的警告。默认值: 1024 * 1024
    • encoding <字符串> 用于所有 stdio 输入和输出的编码。默认值: 'buffer'
    • shell <布尔值> | <字符串> 如果为 true,则在 shell 内运行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。不同的 shell 可以指定为字符串。参见 Shell 要求默认 Windows shell默认值: false(无 shell)。
    • windowsVerbatimArguments <布尔值> 在 Windows 上不进行参数的引用或转义。在 Unix 上忽略。当指定 shell 并为 CMD 时,此值自动设置为 true默认值: false
    • windowsHide <布尔值> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。默认值: false
  • 返回值: <对象>

    • pid <数字> 子进程的进程 ID。
    • output <数组> 来自 stdio 输出的结果数组。
    • stdout <Buffer> | <字符串> output[1] 的内容。
    • stderr <Buffer> | <字符串> output[2] 的内容。
    • status <数字> | <null> 子进程的退出代码,如果子进程由于信号而终止,则为 null
    • signal <字符串> | <null> 用于终止子进程的信号,如果子进程未因信号而终止,则为 null
    • error <错误> 如果子进程失败或超时,则为错误对象。

child_process.spawnSync() 方法通常与 child_process.spawn() 相同,区别在于该函数只有在子进程完全关闭后才会返回。当遇到超时并发送 killSignal 时,该方法只有在进程完全退出后才会返回。如果进程拦截并处理 SIGTERM 信号并且没有退出,则父进程将等待子进程退出。

如果启用了shell选项,请勿将未经消毒的用户输入传递给此函数。任何包含 shell 元字符的输入都可能用于触发任意命令执行。

类: ChildProcess

新增于: v2.2.0

ChildProcess 的实例代表已生成的子进程。

不应直接创建 ChildProcess 的实例。而是使用 child_process.spawn()child_process.exec()child_process.execFile()child_process.fork() 方法来创建 ChildProcess 的实例。

事件: 'close'

新增于: v0.7.7

  • code <number> 如果子进程自行退出,则为退出代码。
  • signal <string> 用于终止子进程的信号。

'close' 事件在进程结束 子进程的 stdio 流已关闭后发出。这与 'exit' 事件不同,因为多个进程可能共享相同的 stdio 流。'close' 事件将在 'exit' 事件发出之后或如果子进程未能生成则在 'error' 事件之后发出。

js
const { spawn } = require('node:child_process')
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.on('close', code => {
  console.log(`子进程关闭所有 stdio,代码为 ${code}`)
})

ls.on('exit', code => {
  console.log(`子进程退出,代码为 ${code}`)
})
js
import { spawn } from 'node:child_process'
const ls = spawn('ls', ['-lh', '/usr'])

ls.stdout.on('data', data => {
  console.log(`stdout: ${data}`)
})

ls.on('close', code => {
  console.log(`子进程关闭所有 stdio,代码为 ${code}`)
})

ls.on('exit', code => {
  console.log(`子进程退出,代码为 ${code}`)
})

事件:'disconnect'

新增于:v0.7.2

'disconnect' 事件在父进程调用 subprocess.disconnect() 方法或子进程调用 process.disconnect() 方法后发出。断开连接后,将无法再发送或接收消息,并且 subprocess.connected 属性为 false

事件:'error'

'error' 事件在以下情况下发出:

  • 无法生成进程。
  • 无法终止进程。
  • 向子进程发送消息失败。
  • 子进程通过 signal 选项被中止。

在发生错误后,'exit' 事件可能会也可能不会触发。当同时监听 'exit''error' 事件时,请注意避免意外多次调用处理程序函数。

另见 subprocess.kill()subprocess.send()

事件:'exit'

新增于:v0.1.90

  • code <数字> 如果子进程自行退出,则为退出代码。
  • signal <字符串> 子进程被终止的信号。

'exit' 事件在子进程结束之后发出。如果进程退出,则 code 为进程的最终退出代码,否则为 null。如果进程由于接收到信号而终止,则 signal 为信号的字符串名称,否则为 null。两者之一始终非 null

当触发 'exit' 事件时,子进程的 stdio 流可能仍然打开。

Node.js 为 SIGINTSIGTERM 建立信号处理程序,并且 Node.js 进程不会因接收到这些信号而立即终止。相反,Node.js 将执行一系列清理操作,然后重新引发已处理的信号。

参见 waitpid(2)

事件:'message'

新增于:v0.5.9

当子进程使用 process.send() 发送消息时,将触发 'message' 事件。

消息会经过序列化和解析。最终的消息可能与最初发送的消息不同。

如果在生成子进程时将 serialization 选项设置为 'advanced',则 message 参数可以包含 JSON 无法表示的数据。详情请参见 高级序列化

事件:'spawn'

新增于:v15.1.0, v14.17.0

'spawn' 事件在子进程成功生成后发出一次。如果子进程未能成功生成,则不会发出 'spawn' 事件,而是发出 'error' 事件。

如果发出,'spawn' 事件会先于所有其他事件以及通过 stdoutstderr 接收到的任何数据。

无论在生成的进程内部是否发生错误,'spawn' 事件都会触发。例如,如果 bash some-command 成功生成,则会触发 'spawn' 事件,尽管 bash 可能未能成功生成 some-command。此警告也适用于使用 { shell: true } 的情况。

subprocess.channel

[历史]

版本变更
v14.0.0此对象不再意外地暴露原生 C++ 绑定。
v7.1.0新增于:v7.1.0
  • <对象> 代表与子进程的 IPC 通道的管道。

subprocess.channel 属性是对子进程 IPC 通道的引用。如果不存在 IPC 通道,则此属性为 undefined

subprocess.channel.ref()

新增于:v7.1.0

此方法使得 IPC 通道在之前调用了 .unref() 后仍然保持父进程的事件循环运行。

subprocess.channel.unref()

新增于:v7.1.0

此方法使得 IPC 通道不保持父进程的事件循环运行,即使通道处于打开状态,也允许其结束。

subprocess.connected

新增于:v0.7.2

  • <布尔值> 调用 subprocess.disconnect() 后设置为 false

subprocess.connected 属性指示是否仍然可以向子进程发送和接收消息。当 subprocess.connectedfalse 时,将无法再发送或接收消息。

subprocess.disconnect()

新增于:v0.7.2

关闭父进程和子进程之间的 IPC 通道,允许子进程在没有其他连接保持其存活状态时优雅地退出。调用此方法后,父进程和子进程中的 subprocess.connectedprocess.connected 属性(分别)将被设置为 false,并且将无法再在进程之间传递消息。

当没有正在接收的消息时,将发出 'disconnect' 事件。这通常会在调用 subprocess.disconnect() 后立即触发。

当子进程是 Node.js 实例(例如,使用 child_process.fork() 生成的)时,可以在子进程中调用 process.disconnect() 方法来关闭 IPC 通道。

subprocess.exitCode

subprocess.exitCode 属性指示子进程的退出代码。如果子进程仍在运行,则该字段将为 null

subprocess.kill([signal])

新增于:v0.1.90

subprocess.kill() 方法向子进程发送信号。如果没有给出参数,则将向进程发送 'SIGTERM' 信号。有关可用信号的列表,请参见 signal(7)。如果 kill(2) 成功,则此函数返回 true,否则返回 false

js
const { spawn } = require('node:child_process')
const grep = spawn('grep', ['ssh'])

grep.on('close', (code, signal) => {
  console.log(`子进程由于收到信号 ${signal} 而终止`)
})

// 向进程发送 SIGHUP。
grep.kill('SIGHUP')
js
import { spawn } from 'node:child_process'
const grep = spawn('grep', ['ssh'])

grep.on('close', (code, signal) => {
  console.log(`子进程由于收到信号 ${signal} 而终止`)
})

// 向进程发送 SIGHUP。
grep.kill('SIGHUP')

如果无法传递信号,则 ChildProcess 对象可能会发出 'error' 事件。向已退出的子进程发送信号不是错误,但可能会产生不可预见的后果。具体来说,如果进程标识符 (PID) 已重新分配给另一个进程,则信号将传递给该进程,这可能会产生意外的结果。

虽然该函数称为 kill,但传递给子进程的信号实际上可能不会终止进程。

请参考 kill(2)

在 Windows 上,由于不存在 POSIX 信号,因此将忽略 signal 参数,但 'SIGKILL''SIGTERM''SIGINT''SIGQUIT' 除外,并且进程将始终被强制且突然终止(类似于 'SIGKILL')。有关详细信息,请参见 信号事件

在 Linux 上,当尝试终止其父进程时,子进程的子进程不会被终止。当在 shell 中运行新进程或使用 ChildProcessshell 选项时,这很可能会发生:

js
const { spawn } = require('node:child_process')

const subprocess = spawn(
  'sh',
  [
    '-c',
    `node -e "setInterval(() => {
      console.log(process.pid, 'is alive')
    }, 500);"`,
  ],
  {
    stdio: ['inherit', 'inherit', 'inherit'],
  }
)

setTimeout(() => {
  subprocess.kill() // 不会终止 shell 中的 Node.js 进程。
}, 2000)
js
import { spawn } from 'node:child_process'

const subprocess = spawn(
  'sh',
  [
    '-c',
    `node -e "setInterval(() => {
      console.log(process.pid, 'is alive')
    }, 500);"`,
  ],
  {
    stdio: ['inherit', 'inherit', 'inherit'],
  }
)

setTimeout(() => {
  subprocess.kill() // 不会终止 shell 中的 Node.js 进程。
}, 2000)

subprocess[Symbol.dispose]()

新增于: v20.5.0, v18.18.0

[稳定性: 1 - 实验性]

稳定性: 1 稳定性: 1 - 实验性

使用'SIGTERM'调用subprocess.kill()

subprocess.killed

新增于: v0.5.10

  • <布尔值> subprocess.kill()成功向子进程发送信号后设置为true

subprocess.killed属性指示子进程是否成功接收到来自subprocess.kill()的信号。killed属性并不表示子进程已被终止。

subprocess.pid

新增于: v0.1.90

返回子进程的进程标识符 (PID)。如果由于错误导致子进程未能生成,则值为undefined,并发出error事件。

js
const { spawn } = require('node:child_process')
const grep = spawn('grep', ['ssh'])

console.log(`Spawned child pid: ${grep.pid}`)
grep.stdin.end()
js
import { spawn } from 'node:child_process'
const grep = spawn('grep', ['ssh'])

console.log(`Spawned child pid: ${grep.pid}`)
grep.stdin.end()

subprocess.ref()

新增于:v0.7.10

在调用 subprocess.unref() 后调用 subprocess.ref() 将会恢复子进程移除的引用计数,强制父进程等待子进程退出后再自身退出。

js
const { spawn } = require('node:child_process')
const process = require('node:process')

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()
subprocess.ref()
js
import { spawn } from 'node:child_process'
import process from 'node:process'

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()
subprocess.ref()

subprocess.send(message[, sendHandle[, options]][, callback])

[历史]

版本变更
v5.8.0现在支持 options 参数,特别是 keepOpen 选项。
v5.0.0此方法现在返回布尔值以进行流控制。
v4.0.0现在支持 callback 参数。
v0.5.9新增于:v0.5.9

当在父进程和子进程之间建立了 IPC 通道(即使用 child_process.fork() 时),可以使用 subprocess.send() 方法向子进程发送消息。当子进程是 Node.js 实例时,这些消息可以通过 'message' 事件接收。

消息会经过序列化和解析。结果消息可能与最初发送的消息不同。

例如,在父脚本中:

js
const { fork } = require('node:child_process')
const forkedProcess = fork(`${__dirname}/sub.js`)

forkedProcess.on('message', message => {
  console.log('PARENT got message:', message)
})

// 导致子进程打印:CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' })
js
import { fork } from 'node:child_process'
const forkedProcess = fork(`${import.meta.dirname}/sub.js`)

forkedProcess.on('message', message => {
  console.log('PARENT got message:', message)
})

// 导致子进程打印:CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' })

然后子脚本 'sub.js' 可能如下所示:

js
process.on('message', message => {
  console.log('CHILD got message:', message)
})

// 导致父进程打印:PARENT got message: { foo: 'bar', baz: null }
process.send({ foo: 'bar', baz: NaN })

子 Node.js 进程将拥有自己的 process.send() 方法,允许子进程向父进程发送消息。

发送 {cmd: 'NODE_foo'} 消息时有一个特殊情况。在 cmd 属性中包含 NODE_ 前缀的消息保留供 Node.js 内核使用,并且不会在子进程的 'message' 事件中发出。相反,此类消息使用 'internalMessage' 事件发出,并由 Node.js 内部使用。应用程序应避免使用此类消息或侦听 'internalMessage' 事件,因为它可能会在未经通知的情况下发生更改。

可以传递给 subprocess.send() 的可选 sendHandle 参数用于将 TCP 服务器或套接字对象传递给子进程。子进程将接收该对象作为注册在 'message' 事件上的回调函数传递的第二个参数。套接字中接收和缓冲的任何数据都不会发送到子进程。在 Windows 上不支持发送 IPC 套接字。

可选的 callback 是一个函数,在消息发送后但在子进程可能已接收它之前调用。该函数使用单个参数调用:成功时为 null,失败时为 Error 对象。

如果没有提供 callback 函数并且无法发送消息,则 ChildProcess 对象将发出 'error' 事件。例如,当子进程已退出时,就会发生这种情况。

如果通道已关闭或未发送消息的积压超过使得发送更多消息不明智的阈值,则 subprocess.send() 将返回 false。否则,该方法返回 truecallback 函数可用于实现流控制。

示例:发送服务器对象

sendHandle 参数可以用于将 TCP 服务器对象的句柄传递给子进程,如下例所示:

js
const { fork } = require('node:child_process')
const { createServer } = require('node:net')

const subprocess = fork('subprocess.js')

// 打开服务器对象并发送句柄。
const server = createServer()
server.on('connection', socket => {
  socket.end('handled by parent')
})
server.listen(1337, () => {
  subprocess.send('server', server)
})
js
import { fork } from 'node:child_process'
import { createServer } from 'node:net'

const subprocess = fork('subprocess.js')

// 打开服务器对象并发送句柄。
const server = createServer()
server.on('connection', socket => {
  socket.end('handled by parent')
})
server.listen(1337, () => {
  subprocess.send('server', server)
})

子进程将接收服务器对象,如下所示:

js
process.on('message', (m, server) => {
  if (m === 'server') {
    server.on('connection', socket => {
      socket.end('handled by child')
    })
  }
})

一旦服务器在父进程和子进程之间共享,一些连接可以由父进程处理,另一些可以由子进程处理。

虽然上面的例子使用的是 node:net 模块创建的服务器,但 node:dgram 模块服务器使用完全相同的流程,区别在于监听 'message' 事件而不是 'connection' 事件,并且使用 server.bind() 而不是 server.listen()。但是,这仅在 Unix 平台上受支持。

示例:发送套接字对象

类似地,sendHandler 参数可用于将套接字句柄传递给子进程。以下示例生成两个子进程,每个子进程分别处理具有“普通”或“特殊”优先级的连接:

js
const { fork } = require('node:child_process')
const { createServer } = require('node:net')

const normal = fork('subprocess.js', ['normal'])
const special = fork('subprocess.js', ['special'])

// 打开服务器并将套接字发送到子进程。使用 pauseOnConnect 阻止
// 套接字在发送到子进程之前被读取。
const server = createServer({ pauseOnConnect: true })
server.on('connection', socket => {
  // 如果这是特殊优先级...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket)
    return
  }
  // 这是普通优先级。
  normal.send('socket', socket)
})
server.listen(1337)
js
import { fork } from 'node:child_process'
import { createServer } from 'node:net'

const normal = fork('subprocess.js', ['normal'])
const special = fork('subprocess.js', ['special'])

// 打开服务器并将套接字发送到子进程。使用 pauseOnConnect 阻止
// 套接字在发送到子进程之前被读取。
const server = createServer({ pauseOnConnect: true })
server.on('connection', socket => {
  // 如果这是特殊优先级...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket)
    return
  }
  // 这是普通优先级。
  normal.send('socket', socket)
})
server.listen(1337)

subprocess.js 将接收作为事件回调函数传递的第二个参数的套接字句柄:

js
process.on('message', (m, socket) => {
  if (m === 'socket') {
    if (socket) {
      // 检查客户端套接字是否存在。
      // 套接字可能在发送时和在子进程中接收时之间关闭。
      socket.end(`Request handled with ${process.argv[2]} priority`)
    }
  }
})

不要在已传递给子进程的套接字上使用 .maxConnections。父进程无法跟踪套接字何时被销毁。

任何子进程中的 'message' 处理程序都应验证 socket 是否存在,因为连接可能在将其发送到子进程所需的时间内关闭。

subprocess.signalCode

subprocess.signalCode 属性指示子进程接收到的信号(如果有),否则为 null

subprocess.spawnargs

subprocess.spawnargs 属性表示启动子进程时使用的完整命令行参数列表。

subprocess.spawnfile

subprocess.spawnfile 属性指示启动的子进程的可执行文件名。

对于 child_process.fork(),其值将等于 process.execPath。对于 child_process.spawn(),其值将是可执行文件的名称。对于 child_process.exec(),其值将是启动子进程的 shell 的名称。

subprocess.stderr

新增于:v0.1.90

一个表示子进程 stderrReadable Stream

如果子进程以 stdio[2] 设置为除 'pipe' 之外的任何值启动,则此值为 null

subprocess.stderrsubprocess.stdio[2] 的别名。这两个属性都指向相同的值。

如果无法成功启动子进程,则 subprocess.stderr 属性可能为 nullundefined

subprocess.stdin

新增于:v0.1.90

一个表示子进程 stdinWritable Stream

如果子进程等待读取所有输入,则在通过 end() 关闭此流之前,子进程将不会继续。

如果子进程以 stdio[0] 设置为除 'pipe' 之外的任何值启动,则此值为 null

subprocess.stdinsubprocess.stdio[0] 的别名。这两个属性都指向相同的值。

如果无法成功启动子进程,则 subprocess.stdin 属性可能为 nullundefined

subprocess.stdio

新增于:v0.7.10

一个稀疏数组,包含子进程的管道,对应于传递给 child_process.spawn()stdio 选项中设置为 'pipe' 值的位置。subprocess.stdio[0]subprocess.stdio[1]subprocess.stdio[2] 也分别可用作 subprocess.stdinsubprocess.stdoutsubprocess.stderr

在下面的示例中,只有子进程的 fd 1 (stdout) 被配置为管道,因此只有父进程的 subprocess.stdio[1] 是流,数组中的所有其他值都是 null

js
const assert = require('node:assert')
const fs = require('node:fs')
const child_process = require('node:child_process')

const subprocess = child_process.spawn('ls', {
  stdio: [
    0, // 使用父进程的 stdin 用于子进程。
    'pipe', // 将子进程的 stdout 管道到父进程。
    fs.openSync('err.out', 'w'), // 将子进程的 stderr 直接定向到文件。
  ],
})

assert.strictEqual(subprocess.stdio[0], null)
assert.strictEqual(subprocess.stdio[0], subprocess.stdin)

assert(subprocess.stdout)
assert.strictEqual(subprocess.stdio[1], subprocess.stdout)

assert.strictEqual(subprocess.stdio[2], null)
assert.strictEqual(subprocess.stdio[2], subprocess.stderr)
js
import assert from 'node:assert'
import fs from 'node:fs'
import child_process from 'node:child_process'

const subprocess = child_process.spawn('ls', {
  stdio: [
    0, // 使用父进程的 stdin 用于子进程。
    'pipe', // 将子进程的 stdout 管道到父进程。
    fs.openSync('err.out', 'w'), // 将子进程的 stderr 直接定向到文件。
  ],
})

assert.strictEqual(subprocess.stdio[0], null)
assert.strictEqual(subprocess.stdio[0], subprocess.stdin)

assert(subprocess.stdout)
assert.strictEqual(subprocess.stdio[1], subprocess.stdout)

assert.strictEqual(subprocess.stdio[2], null)
assert.strictEqual(subprocess.stdio[2], subprocess.stderr)

如果无法成功生成子进程,则 subprocess.stdio 属性可以为 undefined

subprocess.stdout

新增于:v0.1.90

表示子进程 stdoutReadable Stream

如果子进程以 stdio[1] 设置为除 'pipe' 之外的任何值生成,则这将为 null

subprocess.stdoutsubprocess.stdio[1] 的别名。这两个属性都将引用相同的值。

js
const { spawn } = require('node:child_process')

const subprocess = spawn('ls')

subprocess.stdout.on('data', data => {
  console.log(`Received chunk ${data}`)
})
js
import { spawn } from 'node:child_process'

const subprocess = spawn('ls')

subprocess.stdout.on('data', data => {
  console.log(`Received chunk ${data}`)
})

如果无法成功生成子进程,则 subprocess.stdout 属性可以为 nullundefined

subprocess.unref()

新增于:v0.7.10

默认情况下,父进程会等待分离的子进程退出。为了防止父进程等待特定 subprocess 退出,可以使用 subprocess.unref() 方法。这样做会导致父进程的事件循环不将子进程包含在其引用计数中,允许父进程独立于子进程退出,除非子进程和父进程之间存在已建立的 IPC 通道。

js
const { spawn } = require('node:child_process')
const process = require('node:process')

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()
js
import { spawn } from 'node:child_process'
import process from 'node:process'

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
})

subprocess.unref()

maxBuffer 和 Unicode

maxBuffer 选项指定 stdoutstderr 上允许的最大字节数。如果超过此值,则子进程将被终止。这会影响包含多字节字符编码(如 UTF-8 或 UTF-16)的输出。例如,console.log('中文测试') 将发送 13 个 UTF-8 编码字节到 stdout,尽管只有 4 个字符。

Shell 要求

Shell 应该理解 -c 开关。如果 Shell 是 'cmd.exe',它应该理解 /d /s /c 开关,并且命令行解析应该兼容。

默认 Windows Shell

尽管 Microsoft 规定 %COMSPEC% 必须包含根环境中 'cmd.exe' 的路径,但子进程并不总是受相同的约束。因此,在可以生成 Shell 的 child_process 函数中,如果 process.env.ComSpec 不可用,则使用 'cmd.exe' 作为后备。

高级序列化

新增于:v13.2.0, v12.16.0

子进程支持一种基于 node:v8 模块的序列化 API 的 IPC 序列化机制,该机制基于 HTML 结构化克隆算法。这通常更强大,并支持更多内置 JavaScript 对象类型,例如 BigIntMapSetArrayBufferTypedArrayBufferErrorRegExp 等。

但是,此格式并非 JSON 的完整超集,例如,在此类内置类型的对象上设置的属性不会通过序列化步骤传递。此外,根据传递数据的结构,性能可能与 JSON 不等效。因此,此功能需要通过在调用 child_process.spawn()child_process.fork() 时将 serialization 选项设置为 'advanced' 来启用。