Node.js v10.15.3 文档


child_process(子进程)#

中英对照提交修改

稳定性: 2 - 稳定

child_process 模块提供了以与 popen(3) 类似但不相同的方式衍生子进程的功能。 此功能主要由 child_process.spawn() 函数提供:

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

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

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

ls.on('close', (code) => {
  console.log(`子进程退出码:${code}`);
});

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

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

为方便起见, 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 上运行时,可以使用带有 shell 选项集的 child_process.spawn()、或使用 child_process.exec() 或通过衍生 cmd.exe 并将 .bat.cmd 文件作为参数传入(也就是 shell 选项和 child_process.exec() 所做的)。 在任何情况下,如果脚本文件名包含空格,则需要加上引号。

// 仅限 Windows 系统。
const { spawn } = require('child_process');
const bat = spawn('cmd.exe', ['/c', 'my.bat']);

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

bat.stderr.on('data', (data) => {
  console.log(data.toString());
});

bat.on('exit', (code) => {
  console.log(`子进程退出码:${code}`);
});
// 或:
const { exec } = require('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])#

中英对照提交修改

衍生一个 shell 然后在该 shell 中执行 command,并缓冲产生的输出。 传给 exec 函数的 command 字符串由 shell 直接处理,特殊字符(因 shell 而异)需要相应地处理:

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 属性是子进程的退出码, error.signal 是终止进程的信号。 除 0 以外的任何退出码都被视为出错。

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

const { exec } = require('child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`执行出错: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

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

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

如果调用此方法的 util.promisify() 版本,则返回的 Promise 会返回具有 stdout 属性和 stderr 属性的 Object。 如果出现错误(包括导致退出码不是 0 的任何错误),则返回被拒绝的 Promise,并在回调中给定相同的 error 对象,但是还有另外两个属性 stdoutstderr

const util = require('util');
const exec = util.promisify(require('child_process').exec);

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

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

中英对照提交修改

child_process.execFile() 函数类似于 child_process.exec(),但默认情况下不会衍生 shell。 相反,指定的可执行 file 直接作为新进程衍生,使其比 child_process.exec() 稍微更高效。

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

const { execFile } = require('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 会返回具有 stdout 属性和 stderr 属性的 Object。 如果出现错误(包括导致退出码不是 0 的任何错误),则返回被拒绝的 Promise,并在回调中给定相同的 error 对象,但是还有另外两个属性 stdoutstderr

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

如果启用了 shell 选项,则不要将未经过处理的用户输入传给此函数。 包含 shell 元字符的任何输入都可用于触发任意命令执行。

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

中英对照提交修改

  • modulePath <string> 要在子进程中运行的模块。
  • args <string[]> 字符串参数的列表。
  • options <Object>

    • cwd <string> 子进程的当前工作目录。
    • detached <boolean> 准备子进程独立于其父进程运行。具体行为取决于平台,参阅 options.detached
    • env <Object> 环境变量的键值对。
    • execPath <string> 用于创建子进程的可执行文件。
    • execArgv <string[]> 传给可执行文件的字符串参数的列表。默认值: process.execArgv
    • silent <boolean> 如果为 true,则子进程的 stdin、stdout、stderr 会被输送到父进程,否则它们会继承自父进程,详见 child_process.spawn()stdio 中的 'pipe''inherit' 选项。默认值: false
    • stdio <Array> | <string> 参阅 child_process.spawn()stdio。当提供此选项时,则它覆盖 silent 选项。如果使用了数组变量,则它必须包含一个值为 'ipc' 的元素,否则会抛出错误。例如 [0, 1, 2, 'ipc']
    • windowsVerbatimArguments <boolean> 在 Windows 上不为参数加上引号或转义。在 Unix 上则忽略。默认值: false
    • uid <number> 设置进程的用户标识,参阅 setuid(2)
    • gid <number> 设置进程的群组标识,参阅 setgid(2)
  • 返回: <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 标识)与父进程通信。

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

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

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

中英对照提交修改

  • command <string> 要运行的命令。
  • args <string[]> 字符串参数的列表。
  • options <Object>

    • cwd <string> 子进程的当前工作目录。
    • env <Object> 环境变量的键值对。
    • argv0 <string> 显式设置发送给子进程的 argv[0] 的值。如果没有指定,则设置为 command 的值。
    • stdio <Array> | <string> 子进程的 stdio 配置。参阅 options.stdio
    • detached <boolean> 准备子进程独立于其父进程运行。具体行为取决于平台,参阅 options.detached
    • uid <number> 设置进程的用户标识,参阅 setuid(2)
    • gid <number> 设置进程的群组标识,参阅 setgid(2)
    • shell <boolean> | <string> 如果为 true,则在 shell 中运行 command。 在 UNIX 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。 传入字符串则指定其他 shell。 参阅 shell 的要求Windows 默认的 shell默认值: false(没有 shell)。
    • windowsVerbatimArguments <boolean> 在 Windows 上不为参数加上引号或转义。在 Unix 上忽略。如果指定了 shell,则自动设为 true默认值: false
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程的控制台窗口。默认值: false
  • 返回: <ChildProcess>

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

如果启用了 shell 选项,则不要将未经过处理的用户输入传给此函数。 包含 shell 元字符的任何输入都可用于触发任意命令执行。

第三个参数可用于指定其他选项,具有以下默认值:

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

使用 cwd 指定衍生进程的工作目录。 如果没有给出,则默认为继承当前工作目录。

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

env 中的 undefined 值会被忽略。

示例,运行 ls -lh /usr,并捕获 stdoutstderr、以及退出码:

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

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

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

ls.on('close', (code) => {
  console.log(`子进程退出码:${code}`);
});

示例,一种非常精细的运行 ps ax | grep ssh 的方式:

const { spawn } = require('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.log(`ps stderr: ${data}`);
});

ps.on('close', (code) => {
  if (code !== 0) {
    console.log(`ps 进程的退出码:${code}`);
  }
  grep.stdin.end();
});

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

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

grep.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep 进程的退出码:${code}`);
  }
});

示例,检查失败的 spawn

const { spawn } = require('child_process');
const subprocess = spawn('bad_command');

subprocess.on('error', (err) => {
  console.log('无法启动子进程');
});

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

Node.js 一般会在启动时用 process.execPath 覆盖 argv[0],因此 Node.js 子进程的 process.argv[0] 与从父进程传给 spawnargv0 参数不会匹配,可以使用 process.argv0 属性获取。

options.detached#

中英对照提交修改

在 Windows 上,设置 options.detachedtrue 可以使子进程在父进程退出后继续运行。 子进程有自己的控制台窗口。 一旦启用一个子进程,它将不能被禁用。

在非 Windows 平台上,如果 options.detached 设为 true,则子进程会成为新的进程组和会话的领导者。 子进程在父进程退出后可以继续运行,不管它们是否被分离。 详见 setsid(2)

默认情况下,父进程会等待被分离的子进程退出。 为了防止父进程等待 subprocess,可以使用 subprocess.unref()。 这样做会导致父进程的事件循环不包含子进程的引用计数,使得父进程独立于子进程退出,除非子进程和父进程之间建立了一个 IPC 信道。

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

例子,一个长期运行的进程,为了忽视父进程的终止,通过分离且忽视其父进程的 stdio 文件描述符来实现:

const { spawn } = require('child_process');

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

subprocess.unref();

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

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

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

subprocess.unref();

options.stdio#

中英对照提交修改

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

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

  • 'pipe' - 相当于 ['pipe', 'pipe', 'pipe'](默认值)。
  • 'ignore' - 相当于 ['ignore', 'ignore', 'ignore']
  • 'inherit' - 相当于 ['inherit', 'inherit', 'inherit'][0, 1, 2]

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

  1. 'pipe' - 在子进程和父进程之间创建一个管道。 管道的父端作为 child_process 对象上的属性 subprocess.stdio[fd] 暴露给父进程。 为 fd 0 - 2 创建的管道也可分别作为 subprocess.stdinsubprocess.stdoutsubprocess.stderr 使用。
  1. 'ipc' - 创建一个 IPC 通道,用于在父进程和子进程之间传递消息或文件描述符。 一个 ChildProcess 最多可以有一个 IPC stdio 文件描述符。 设置此选项将启用 subprocess.send() 方法。 如果子进程是 Node.js 进程,则 IPC 通道的存在将启用 process.send()process.disconnect() 方法、以及子进程内的 'disconnect''message' 事件。

    不支持以 process.send() 以外的任何方式访问 IPC 通道 fd,或者使用不具有 Node.js 实例的子进程使用 IPC 通道。

  2. 'ignore' - 指示 Node.js 忽略子进程中的 fd。 虽然 Node.js 将始终为它衍生的进程打开 fd 0 - 2,但将 fd 设置为 'ignore' 将导致 Node.js 打开 /dev/null 并将其附加到子进程的 fd。

  3. 'inherit' - 将相应的 stdio 流传给父进程或从父进程传入。 在前三个位置,这分别相当于 process.stdinprocess.stdoutprocess.stderr。 在任何其他位置则相当于 'ignore'

  4. <Stream> 对象 - 与子进程共享指向 tty、文件、套接字或管道的可读或可写流。 流的底层文件描述符在子进程中复制到与 stdio 数组中的索引对应的 fd。 注意,流必须具有底层描述符(文件流直到触发 'open' 事件才需要)。

  5. 正整数 - 整数值被解释为当前在父进程中打开的文件描述符。 它与子进程共享,类似于共享 <Stream> 对象。

  6. nullundefined - 使用默认值。 对于 stdio 的 fd 0、1 和 2(换句话说,stdin、stdout 和 stderr),将创建一个管道。 对于 fd 3 及更高版本,默认为 'ignore'

const { spawn } = require('child_process');

// 子进程使用父进程的 stdio。
spawn('prg', [], { stdio: 'inherit' });

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

// 打开一个额外的 fd=4,与呈现启动式界面的程序进行交互。
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });

注意,当在父进程和子进程之间建立 IPC 通道,并且子进程是 Node.js 进程时,子进程在未指向(使用 unref())IPC 通道的情况下启动直到子进程注册事件处理函数用于 'disconnect' 事件或 'message' 事件。 这允许子进程正常退出而不需要通过开放的 IPC 通道保持打开该进程。

在类 UNIX 操作系统上,child_process.spawn() 方法在将事件循环与子进程解耦之前会同步地执行内存操作。 具有大内存占用的应用程序可能会发现频繁的 child_process.spawn() 调用成为瓶颈。 有关更多信息,参阅 V8 问题 7381

还可参阅:child_process.exec()child_process.fork()

创建同步进程#

中英对照提交修改

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

阻塞这些调用对于简化通用脚本任务和简化启动时应用程序配置的加载或处理非常有用。

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

中英对照提交修改

  • file <string> 要运行的可执行文件的名称或路径。
  • args <string[]> 字符串参数的列表。
  • options <Object>

    • cwd <string> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传给衍生进程的值。提供此值则会覆盖 stdio[0]
    • stdio <string> | <Array> 子进程的 stdio 配置。默认情况下, stderr 将输出到父进程的 stderr,除非指定了 stdio默认值: 'pipe'
    • env <Object> 环境变量的键值对。
    • uid <number> 设置进程的用户标识,参阅 setuid(2)
    • gid <number> 设置进程的群组标识,参阅 setgid(2)
    • timeout <number> 允许进程运行的最长时间,以毫秒为单位。默认值: undefined
    • killSignal <string> | <integer> 衍生的进程将被终止时使用的信号值。默认值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 允许的最大字节数。如果超过限制,则子进程会终止。参阅 maxBuffer 与 Unicode默认值: 200 * 1024
    • encoding <string> 用于所有 stdio 输入和输出的字符编码。默认值: 'buffer'
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程的控制台窗口。默认值: false
    • shell <boolean> | <string> 如果为 true,则在 shell 中运行 command。 在 UNIX 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。 传入字符串则指定其他 shell。 参阅 shell 的要求Windows 默认的 shell默认值: false(没有 shell)。
  • 返回: <Buffer> | <string> 命令的 stdout。

child_process.execFileSync() 方法通常与 child_process.execFile() 相同,但该方法在子进程完全关闭之前不会返回。 当遇到超时并发送 killSignal 时,该方法也需等到进程完全退出后才返回。

如果子进程拦截并处理了 SIGTERM 信号但未退出,则父进程仍将等待子进程退出。

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

如果启用了 shell 选项,则不要将未经过处理的用户输入传给此函数。 包含 shell 元字符的任何输入都可用于触发任意命令执行。

child_process.execSync(command[, options])#

中英对照提交修改

  • command <string> 要运行的命令。
  • options <Object>

    • cwd <string> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传给衍生进程的值。提供此值则会覆盖 stdio[0]
    • stdio <string> | <Array> 子进程的 stdio 配置。默认情况下, stderr 将输出到父进程的 stderr,除非指定了 stdio默认值: 'pipe'
    • env <Object> 环境变量的键值对。
    • shell <string> 用于执行命令的 shell。参阅 shell 的要求Windows 默认的 shell默认值: UNIX 上是 '/bin/sh',Windows 上是 process.env.ComSpec
    • uid <number> 设置进程的用户标识,参阅 setuid(2)
    • gid <number> 设置进程的群组标识,参阅 setgid(2)
    • timeout <number> 允许进程运行的最长时间,以毫秒为单位。默认值: undefined
    • killSignal <string> | <integer> 衍生的进程将被终止时使用的信号值。默认值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 允许的最大字节数。如果超过限制,则子进程会终止。参阅 maxBuffer 与 Unicode默认值: 200 * 1024
    • encoding <string> 用于所有 stdio 输入和输出的字符编码。默认值: 'buffer'
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程的控制台窗口。默认值: false
  • 返回: <Buffer> | <string> 命令的 stdout。

child_process.execSync() 方法通常与 child_process.exec() 相同,但该方法在子进程完全关闭之前不会返回。 当遇到超时并发送 killSignal 时,该方法也需等到进程完全退出后才返回。 注意,如果子进程拦截并处理了 SIGTERM 信号但未退出,则父进程将一直等到子进程退出。

如果进程超时或具有非零的退出码,则此方法将抛出错误。 Error 对象将包含 child_process.spawnSync() 的整个结果。

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

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

中英对照提交修改

  • command <string> 要运行的命令。
  • args <string[]> 字符串参数的列表。
  • options <Object>

    • cwd <string> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传给衍生进程的值。提供此值则会覆盖 stdio[0]
    • argv0 <string> 显式设置发送给子进程的 argv[0] 的值。如果没有指定,则设置为 command 的值。
    • stdio <string> | <Array> 子进程的 stdio 配置。
    • env <Object> 环境变量的键值对。
    • uid <number> 设置进程的用户标识,参阅 setuid(2)
    • gid <number> 设置进程的群组标识,参阅 setgid(2)
    • timeout <number> 允许进程运行的最长时间,以毫秒为单位。默认值: undefined
    • killSignal <string> | <integer> 衍生的进程将被终止时使用的信号值。默认值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 允许的最大字节数。如果超过限制,则子进程会终止。参阅 maxBuffer 与 Unicode默认值: 200 * 1024
    • encoding <string> 用于所有 stdio 输入和输出的字符编码。默认值: 'buffer'
    • shell <boolean> | <string> 如果为 true,则在 shell 中运行 command。 在 UNIX 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。 传入字符串则指定其他 shell。 参阅 shell 的要求Windows 默认的 shell默认值: false(没有 shell)。
    • windowsVerbatimArguments <boolean> 在 Windows 上不为参数加上引号或转义。在 Unix 上忽略。如果指定了 shell,则自动设为 true默认值: false
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程的控制台窗口。默认值: false
  • 返回: <Object>

child_process.spawnSync() 方法通常与 child_process.spawn() 相同,但在子进程完全关闭之前函数不会返回。 当遇到超时并发送 killSignal 时,该方法也需等到进程完全退出后才返回。 注意,如果进程拦截并处理了 SIGTERM 信号但未退出,则父进程将一直等到子进程退出。

如果启用了 shell 选项,则不要将未经过处理的用户输入传给此函数。 包含 shell 元字符的任何输入都可用于触发任意命令执行。

ChildProcess 类#

中英对照提交修改

ChildProcess 类的实例都是 EventEmitter,表示衍生的子进程。

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

'close' 事件#

中英对照提交修改

  • code <number> 子进程的退出码。
  • signal <string> 终止子进程的信号。

当子进程的 stdio 流被关闭时触发。 与 'exit' 事件的区别是,多个进程可能共享同一 stdio 流。

'disconnect' 事件#

中英对照提交修改

调用父进程的 subprocess.disconnect() 或子进程的 process.disconnect() 后会触发。 断开连接后就不能再发送或接收信息,且 subprocess.connected 属性会被设为 false

'error' 事件#

中英对照提交修改

当出现以下情况时触发 'error' 事件:

  1. 无法衍生进程;
  2. 无法杀死进程;
  3. 向子进程发送信息失败。

发生错误后, 'exit' 事件可能会也可能不会触发。 如果同时监听了 'exit''error' 事件,可能会多次调用处理函数。

参阅 subprocess.kill()subprocess.send()

'exit' 事件#

中英对照提交修改

  • code <number> 子进程的退出码。
  • signal <string> 终止子进程的信号。

当子进程结束后时触发。 如果进程退出,则 code 是进程的最终退出码,否则为 null。 如果进程是收到的信号而终止,则 signal 是信号的名称,否则为 null。 这两个值至少有一个是非空的。

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

Node.js 建立了 SIGINTSIGTERM 的信号处理程序,且 Node.js 进程收到这些信号也不会立即终止。 Node.js 会执行一系列的清理操作后重新引发处理信号。

参阅 waitpid(2)

'message' 事件#

中英对照提交修改

当子进程使用 process.send() 发送消息时触发。

消息通过序列化和解析传递,收到的消息可能跟发送的不完全一样。

subprocess.channel#

中英对照提交修改

  • <Object> 子进程的 IPC 通道的管道。

返回子进程的 IPC 通道的引用。 如果当前没有 IPC 通道,则返回 undefined

subprocess.connected#

中英对照提交修改

  • <boolean> 调用 subprocess.disconnect() 后会被设为 false

表明是否可以从子进程发送和接收消息。 当 subprocess.connectedfalse 时,则不能再发送或接收消息。

subprocess.disconnect()#

中英对照提交修改

关闭父进程与子进程之间的 IPC 通道,一旦没有其他的连接使其保持活跃,则允许子进程正常退出。 调用该方法后,父进程和子进程上各自的 subprocess.connectedprocess.connected 属性都会被设为 false,且进程之间不能再传递消息。

当正在接收的进程中没有消息时,就会触发 'disconnect' 事件。 这经常在调用 subprocess.disconnect() 后立即被触发。

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

subprocess.kill([signal])#

中英对照提交修改

向子进程发送信号。 如果没有指定参数,则进程会发送 'SIGTERM' 信号。 查看 signal(7) 了解可用的信号列表。

const { spawn } = require('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)

在 Linux 上,当试图杀死父进程时,子进程的子进程不会被终止。 这有可能发生在当在一个 shell 中运行一个新进程时,或使用 ChildProcess 中的 shell 选项时:

'use strict';
const { spawn } = require('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 进程。
}, 2000);

subprocess.killed#

中英对照提交修改

  • <boolean> 当调用 subprocess.kill() 发送信号给子进程后,该值会被设为 true

表明子进程是否已成功接收到 subprocess.kill() 的信号。 但并不表明子进程是否已被终止。

subprocess.pid#

中英对照提交修改

返回子进程的进程标识(PID)。

const { spawn } = require('child_process');
const grep = spawn('grep', ['ssh']);

console.log(`衍生的子进程的 pid:${grep.pid}`);
grep.stdin.end();

subprocess.ref()#

中英对照提交修改

调用 subprocess.unref() 之后再调用 subprocess.ref() 会还原已被移除的子进程引用计数,强迫父进程在退出自身之前等待子进程退出。

const { spawn } = require('child_process');

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

subprocess.unref();
subprocess.ref();

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

中英对照提交修改

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

消息通过序列化和解析进行传递,接收到消息可能跟发送的不完全一样。

例子,父进程脚本如下:

const cp = require('child_process');
const n = cp.fork(`${__dirname}/sub.js`);

n.on('message', (m) => {
  console.log('父进程收到消息', m);
});

// 使子进程输出: 子进程收到消息 { hello: 'world' }
n.send({ hello: 'world' });

子进程脚本 'sub.js' 如下:

process.on('message', (m) => {
  console.log('子进程收到消息', m);
});

// 使父进程输出: 父进程收到消息 { foo: 'bar', baz: null }
process.send({ foo: 'bar', baz: NaN });

Node.js 中的子进程有一个自己的 process.send() 方法,允许子进程发送消息回父进程。

当发送一个 {cmd: 'NODE_foo'} 消息时,是一个特例。 cmd 属性中包含 NODE_ 前缀的消息是预留给 Node.js 核心代码内部使用的,不会触发子进程的 'message' 事件。 而是,这种消息可使用 process.on('internalMessage') 事件触发,且被 Node.js 内部消费。 应用程序应避免使用这种消息或监听 'internalMessage' 事件。

可选的 sendHandle 参数可能被传给 subprocess.send(),它用于传入一个 TCP 服务器或 socket 对象给子进程。 子进程会接收对象作为第二个参数,并传给注册在 'message' 事件上的回调函数。 socket 上接收或缓冲的任何数据不会被发送给子进程。

可选的 callback 是一个函数,它在消息发送之后、子进程收到消息之前被调用。 该函数被调用时只有一个参数:成功时是 null,失败时是一个 Error 对象。

如果没有提供 callback 函数,且消息没被发送,则一个 'error' 事件将被 ChildProcess 对象触发。 这是有可能发生的,例如当子进程已经退出时。

如果通道已关闭,或当未发送的消息的积压超过阈值使其无法发送更多时, subprocess.send() 会返回 false。 除此以外,该方法返回 truecallback 函数可用于实现流量控制。

例子:发送 server 对象#

中英对照提交修改

sendHandle 参数可用于将一个 TCP server 对象句柄传给子进程,如下所示:

const subprocess = require('child_process').fork('subprocess.js');

// 开启 server 对象,并发送该句柄。
const server = require('net').createServer();
server.on('connection', (socket) => {
  socket.end('被父进程处理');
});
server.listen(1337, () => {
  subprocess.send('server', server);
});

子进程接收 server 对象如下:

process.on('message', (m, server) => {
  if (m === 'server') {
    server.on('connection', (socket) => {
      socket.end('被子进程处理');
    });
  }
});

当服务器在父进程和子进程之间是共享的,则一些连接可被父进程处理,另一些可被子进程处理。

上面的例子使用了一个 net 模块创建的服务器,而 dgram 模块的服务器使用完全相同的工作流程,但它监听一个 'message' 事件而不是 'connection' 事件,且使用 server.bind 而不是 server.listen()。 目前仅 UNIX 平台支持这一点。

例子:发送 socket 对象#

中英对照提交修改

同样, sendHandle 参数可用于将一个 socket 句柄传给子进程。 以下例子衍生了两个子进程,分别用于处理 "normal" 连接或优先处理 "special" 连接:

const { fork } = require('child_process');
const normal = fork('subprocess.js', ['normal']);
const special = fork('subprocess.js', ['special']);

// 开启 server,并发送 socket 给子进程。
// 使用 `pauseOnConnect` 防止 socket 在被发送到子进程之前被读取。
const server = require('net').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 会接收到一个 socket 句柄,并作为第二个参数传给事件回调函数:

process.on('message', (m, socket) => {
  if (m === 'socket') {
    if (socket) {
      // 检查客户端 socket 是否存在。
      // socket 在被发送与被子进程接收这段时间内可被关闭。
      socket.end(`请求被 ${process.argv[2]} 优先级处理`);
    }
  }
});

一旦一个 socket 已被传给了子进程,则父进程不再能够跟踪 socket 何时被销毁。 为了表明这个, .connections 属性会变成 null。 当发生这种情况时,建议不要使用 .maxConnections

建议在子进程中的任何 message 处理程序都需要验证 socket 是否存在,因为连接可能会在它在发送给子进程的这段时间内被关闭。

subprocess.stderr#

中英对照提交修改

返回子进程的 stderr 可读流。

如果衍生子进程时 stdio[2] 被设为不是 'pipe' 的值,则返回 null

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

subprocess.stdin#

中英对照提交修改

返回子进程的 stdin 可写流。

如果子进程正在等待读取输入,则子进程不会继续直到流已通过 end() 关闭。

如果衍生子进程时 stdio[0] 被设为不是 'pipe' 的值,则返回 null

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

subprocess.stdio#

中英对照提交修改

一个到子进程的管道的稀疏数组,对应着传给 child_process.spawn() 的选项中值被设为 'pipe'stdiosubprocess.stdio[0]subprocess.stdio[1]subprocess.stdio[2] 分别可用作 subprocess.stdinsubprocess.stdoutsubprocess.stderr

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

const assert = require('assert');
const fs = require('fs');
const child_process = require('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.stdout#

中英对照提交修改

返回子进程的 stdout 可读流。

如果衍生子进程时 stdio[1] 被设为不是 'pipe' 的值,则返回 null

subprocess.stdoutsubprocess.stdio[1] 的别名。 两个属性指向同一个值。

subprocess.unref()#

中英对照提交修改

默认情况下,父进程会等待已解绑的子进程退出。 如果无需父进程等待,可使用 subprocess.unref() 退出 subprocess

const { spawn } = require('child_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 需要理解 UNIX 上的 -c 开关、或 Window 上的 /d /s /c。 在 Windows 上,命令行解析需要兼容 'cmd.exe'

Windows 默认的 shell#

中英对照提交修改

尽管微软指定在根环境中 %COMSPEC% 必须包含 'cmd.exe' 的路径,但子进程并不一定遵循要求。 因此在衍生 shell 的 child_process 函数中,如果没有 process.env.ComSpec,则使用 'cmd.exe'