子进程#

稳定性: 2 - 稳定

源代码: lib/child_process.js

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

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}`);
});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}`);
});

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

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

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

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 上运行时,可以使用设置了 shell 选项的 child_process.spawn()、使用 child_process.exec() 或通过衍生 cmd.exe 并将 .bat.cmd 文件作为参数传递来调用 .bat.cmd 文件(这正是 shell 选项和 child_process.exec() 所做的)。 在任何情况下,如果脚本文件名包含空格,则需要用引号引起来。

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

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

// Script with spaces in the filename:
const bat = spawn('"my script.cmd" a b', { shell: true });
// or:
exec('"my script.cmd" a b', (err, stdout, stderr) => {
  // ...
});// OR...
import { exec, spawn } from 'node:child_process';

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

// Script with spaces in the filename:
const bat = spawn('"my script.cmd" a b', { shell: true });
// or:
exec('"my script.cmd" a b', (err, stdout, stderr) => {
  // ...
});

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

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

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

exec('"/path/to/test file/test.sh" arg1 arg2');
// Double quotes are used so that the space in the path is not interpreted as
// a delimiter of multiple arguments.

exec('echo "The \\$HOME variable is $HOME"');
// The $HOME variable is escaped in the first instance, but not in the second.import { exec } from 'node:child_process';

exec('"/path/to/test file/test.sh" arg1 arg2');
// Double quotes are used so that the space in the path is not interpreted as
// a delimiter of multiple arguments.

exec('echo "The \\$HOME variable is $HOME"');
// The $HOME variable is escaped in the first instance, but not in the second.

永远不要将未经清理的用户输入传递给此函数。 任何包含 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 对象将传递给回调。

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}`);
});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()ed 版本调用,则它会返回一个 Promise,该 Promise 具有带有 stdoutstderr 属性的 Object。 返回的 ChildProcess 实例作为 child 属性附加到 Promise。 如果发生错误(包括导致非 0 退出代码的任何错误),则返回一个被拒绝的 Promise,该 Promise 具有回调中给出的相同 error 对象,但具有两个额外的属性 stdoutstderr

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();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

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

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

  • file <string> 要运行的可执行文件的名称或路径。
  • args <string[]> 字符串参数列表。
  • options <Object>
    • cwd <string> | <URL> 子进程的当前工作目录。
    • env <Object> 环境变量键值对。 默认值: process.env
    • encoding <string> 默认值: 'utf8'
    • timeout <number> 默认值: 0
    • maxBuffer <number> stdout 或 stderr 上允许的最大数据量(以字节为单位)。 如果超过,子进程将被终止,并且任何输出都将被截断。 请参见 maxBuffer 和 Unicode 的注意事项。 默认值: 1024 * 1024
    • killSignal <string> | <integer> 默认值: 'SIGTERM'
    • uid <number> 设置进程的用户身份(参见 setuid(2))。
    • gid <number> 设置进程的组身份(参见 setgid(2))。
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。 默认值: false
    • windowsVerbatimArguments <boolean> 在 Windows 上不对参数进行引用或转义。 在 Unix 上忽略。 默认值: false
    • shell <boolean> | <string> 如果为 true,则在 shell 中运行 command。 在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。 可以将不同的 shell 指定为字符串。 参见 Shell 要求默认 Windows shell默认值: false(无 shell)。
    • signal <AbortSignal> 允许使用 AbortSignal 中止子进程。
  • callback <Function> 进程终止时使用输出调用的函数。
  • 返回: <ChildProcess>

child_process.execFile() 函数类似于 child_process.exec(),除了它默认不衍生 shell。 相反,指定的可执行文件 file 直接作为新进程衍生,使其比 child_process.exec() 稍微有效率。

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

const { execFile } = require('node:child_process');
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error;
  }
  console.log(stdout);
});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()ed 版本调用,则它会返回一个 Promise,该 Promise 具有带有 stdoutstderr 属性的 Object。 返回的 ChildProcess 实例作为 child 属性附加到 Promise。 如果发生错误(包括导致非 0 退出代码的任何错误),则返回一个被拒绝的 Promise,该 Promise 具有回调中给出的相同 error 对象,但具有两个额外的属性 stdoutstderr

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();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

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();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])#

  • modulePath <string> | <URL> 要在子进程中运行的模块。
  • args <string[]> 字符串参数列表。
  • options <Object>
    • cwd <string> | <URL> 子进程的当前工作目录。
    • detached <boolean> 准备子进程以独立于其父进程运行。 具体行为取决于平台(参见 options.detached)。
    • env <Object> 环境变量键值对。 默认值: process.env
    • execPath <string> 用于创建子进程的可执行文件。
    • execArgv <string[]> 传递给可执行文件的字符串参数列表。 默认值: process.execArgv
    • gid <number> 设置进程的组身份(参见 setgid(2))。
    • serialization <string> 指定用于在进程之间发送消息的序列化种类。 可能的值为 'json''advanced'。 有关更多详细信息,请参见 高级序列化默认值: 'json'
    • signal <AbortSignal> 允许使用 AbortSignal 关闭子进程。
    • killSignal <string> | <integer> 当生成进程将被超时或中止信号终止时要使用的信号值。 默认值: 'SIGTERM'
    • 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']
    • uid <number> 设置进程的用户身份(参见 setuid(2))。
    • windowsVerbatimArguments <boolean> 在 Windows 上不对参数进行引用或转义。 在 Unix 上忽略。 默认值: false
    • timeout <number> 进程允许运行的最长时间(以毫秒为单位)。 默认值: 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

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) => {
    // This will be called with err being an AbortError if the controller aborts
  });
  controller.abort(); // Stops the child process
}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) => {
    // This will be called with err being an AbortError if the controller aborts
  });
  controller.abort(); // Stops the child process
}

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

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

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

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

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

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

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

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

env 中的 undefined 值将被忽略。

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

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}`);
});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 的方法

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}`);
  }
});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 的示例

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

subprocess.on('error', (err) => {
  console.error('Failed to start subprocess.');
});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

const { spawn } = require('node:child_process');
const controller = new AbortController();
const { signal } = controller;
const grep = spawn('grep', ['ssh'], { signal });
grep.on('error', (err) => {
  // This will be called with err being an AbortError if the controller aborts
});
controller.abort(); // Stops the child processimport { spawn } from 'node:child_process';
const controller = new AbortController();
const { signal } = controller;
const grep = spawn('grep', ['ssh'], { signal });
grep.on('error', (err) => {
  // This will be called with err being an AbortError if the controller aborts
});
controller.abort(); // Stops the child process
options.detached#

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

在非 Windows 平台上,如果 options.detached 设置为 true,子进程将被设置为新进程组和会话的领导者。 子进程可以在父进程退出后继续运行,无论它们是否已分离。 参阅 setsid(2) 以获取更多信息。

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

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

长时间运行进程的示例,通过分离并忽略其父 stdio 文件描述符,以便忽略父进程的终止

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();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();

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

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();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#

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 以在父进程和子进程之间创建其他管道。 该值为以下之一:

  1. 'pipe':在子进程和父进程之间创建一个管道。 管道的父进程端作为 subprocess.stdio[fd] 的属性暴露给父进程,在 child_process 对象上。 为 fd 0、1 和 2 创建的管道也可作为 subprocess.stdinsubprocess.stdoutsubprocess.stderr 使用。 这些不是实际的 Unix 管道,因此子进程不能通过它们的描述符文件使用它们,例如 /dev/fd/2/dev/stdout

  2. 'overlapped': 与 'pipe' 相同,除了在句柄上设置了 FILE_FLAG_OVERLAPPED 标志。 这对于子进程的 stdio 句柄上的重叠 I/O 是必需的。 有关更多详细信息,请参阅 文档。 这与非 Windows 系统上的 'pipe' 完全相同。

  3. 'ipc':创建一个 IPC 通道,用于在父进程和子进程之间传递消息/文件描述符。 一个 ChildProcess 最多可以有一个 IPC stdio 文件描述符。 设置此选项会启用 subprocess.send() 方法。 如果子进程是 Node.js 实例,则 IPC 通道的存在将启用 process.send()process.disconnect() 方法,以及子进程中的 'disconnect''message' 事件。

    不支持以任何方式访问 IPC 通道 fd,除了 process.send() 或将 IPC 通道与不是 Node.js 实例的子进程一起使用。

  4. 'ignore':指示 Node.js 忽略子进程中的 fd。 虽然 Node.js 总是为它产生的进程打开 fd 0、1 和 2,但将 fd 设置为 'ignore' 将导致 Node.js 打开 /dev/null 并将其附加到子进程的 fd。

  5. 'inherit':将相应的 stdio 流传递到/从父进程传递。 在前三个位置,这分别等效于 process.stdinprocess.stdoutprocess.stderr。 在任何其他位置,等效于 'ignore'

  6. <Stream> 对象:与子进程共享一个可读或可写的流,该流引用 tty、文件、套接字或管道。 流的底层文件描述符在子进程中被复制到与 stdio 数组中的索引相对应的 fd。 该流必须有一个底层描述符(文件流在 'open' 事件发生之前不会启动)。 注意:虽然从技术上讲可以将 stdin 作为可写的或将 stdout/stderr 作为可读的传递,但不建议这样做。 可读和可写流的设计具有不同的行为,不正确地使用它们(例如,在需要可写流的地方传递可读流)可能会导致意外的结果或错误。 不鼓励这种做法,因为它可能导致未定义的行为或如果流遇到错误则会丢弃回调。 始终确保 stdin 用作可写的,而 stdout/stderr 用作可读的,以保持父进程和子进程之间数据的预期流动。

  7. 正整数:整数值被解释为在父进程中打开的文件描述符。 它与子进程共享,类似于可以共享 <Stream> 对象的方式。 在 Windows 上不支持传递套接字。

  8. nullundefined:使用默认值。 对于 stdio fd 0、1 和 2 (换句话说,stdin、stdout 和 stderr)创建一个管道。 对于 fd 3 及以上,默认为 'ignore'

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

// Child will use parent's stdios.
spawn('prg', [], { stdio: 'inherit' });

// Spawn child sharing only stderr.
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// Open an extra fd=4, to interact with programs presenting a
// startd-style interface.
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });import { spawn } from 'node:child_process';
import process from 'node:process';

// Child will use parent's stdios.
spawn('prg', [], { stdio: 'inherit' });

// Spawn child sharing only stderr.
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// Open an extra fd=4, to interact with programs presenting a
// startd-style interface.
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])#

  • file <string> 要运行的可执行文件的名称或路径。
  • args <string[]> 字符串参数列表。
  • options <Object>
    • cwd <string> | <URL> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传递给派生的进程的值。 如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • stdio <string> | <Array> 子进程的 stdio 配置。 参见 child_process.spawn()stdio。 默认情况下,stderr 将输出到父进程的 stderr,除非指定了 stdio默认值: 'pipe'
    • env <Object> 环境变量键值对。 默认值: process.env
    • uid <number> 设置进程的用户身份(参见 setuid(2))。
    • gid <number> 设置进程的组身份(参见 setgid(2))。
    • timeout <number> 进程允许运行的最长时间(以毫秒为单位)。 默认值: undefined
    • killSignal <string> | <integer> 用于杀死派生进程的信号值。 默认值: 'SIGTERM'
    • maxBuffer <number> 允许在 stdout 或 stderr 上的最大数据量(以字节为单位)。 如果超出,则终止子进程。 参见 maxBuffer 和 Unicode 中的警告。 默认值: 1024 * 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 元字符的输入都可能被用来触发任意命令执行。

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

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // Capture stdout and stderr from child process. Overrides the
    // default behavior of streaming child stderr to the parent stderr
    stdio: 'pipe',

    // Use utf8 encoding for stdio pipes
    encoding: 'utf8',
  });

  console.log(stdout);
} catch (err) {
  if (err.code) {
    // Spawning child process failed
    console.error(err.code);
  } else {
    // Child was spawned but exited with non-zero exit code
    // Error contains any stdout and stderr from the child
    const { stdout, stderr } = err;

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

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // Capture stdout and stderr from child process. Overrides the
    // default behavior of streaming child stderr to the parent stderr
    stdio: 'pipe',

    // Use utf8 encoding for stdio pipes
    encoding: 'utf8',
  });

  console.log(stdout);
} catch (err) {
  if (err.code) {
    // Spawning child process failed
    console.error(err.code);
  } else {
    // Child was spawned but exited with non-zero exit code
    // Error contains any stdout and stderr from the child
    const { stdout, stderr } = err;

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

child_process.execSync(command[, options])#

  • command <string> 要运行的命令。
  • options <Object>
    • cwd <string> | <URL> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传递给派生的进程的值。 如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • stdio <string> | <Array> 子进程的 stdio 配置。 参见 child_process.spawn()stdio。 默认情况下,stderr 将输出到父进程的 stderr,除非指定了 stdio默认值: 'pipe'
    • env <Object> 环境变量键值对。 默认值: process.env
    • 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 的注意事项。 默认值: 1024 * 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> | <URL> 子进程的当前工作目录。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 将作为 stdin 传递给派生的进程的值。 如果 stdio[0] 设置为 'pipe',则提供此值将覆盖 stdio[0]
    • argv0 <string> 显式地设置发送到子进程的 argv[0] 的值。 如果未指定,则将设置为 command
    • stdio <string> | <Array> 子进程的 stdio 配置。 参见 child_process.spawn()stdio默认值: 'pipe'
    • env <Object> 环境变量键值对。 默认值: process.env
    • uid <number> 设置进程的用户身份(参见 setuid(2))。
    • gid <number> 设置进程的组身份(参见 setgid(2))。
    • timeout <number> 进程允许运行的最长时间(以毫秒为单位)。 默认值: undefined
    • killSignal <string> | <integer> 用于杀死派生进程的信号值。 默认值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 上允许的最大数据量(以字节为单位)。 如果超过,子进程将被终止,并且任何输出都将被截断。 请参见 maxBuffer 和 Unicode 的注意事项。 默认值: 1024 * 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 且为 CMD,则自动设置为 true默认: false
    • windowsHide <boolean> 隐藏通常在 Windows 系统上创建的子进程控制台窗口。 默认值: false
  • 返回值: <Object>
    • pid <number> 子进程的 Pid。
    • output <Array> 来自 stdio 输出的结果数组。
    • stdout <Buffer> | <string> output[1] 的内容。
    • stderr <Buffer> | <string> output[2] 的内容。
    • status <number> | <null> 子进程的退出码,如果子进程由于信号而终止,则为 null
    • signal <string> | <null> 用于杀死子进程的信号,如果子进程不是由于信号而终止,则为 null
    • error <Error> 如果子进程失败或超时,则为 error 对象。

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

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

类: ChildProcess#

ChildProcess 的实例表示衍生的子进程。

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

事件: 'close'#

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

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

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(`child process close all stdio with code ${code}`);
});

ls.on('exit', (code) => {
  console.log(`child process exited with code ${code}`);
});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(`child process close all stdio with code ${code}`);
});

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

事件: 'disconnect'#

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

事件: 'error'#

在任何时候都会触发 'error' 事件:

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

'exit' 事件可能会或可能不会在发生错误后触发。 监听 'exit''error' 事件时,请防止意外多次调用处理函数。

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

事件: 'exit'#

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

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

触发 'exit' 事件时,子进程 stdio 流可能仍处于打开状态。

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

参见 waitpid(2)

事件: 'message'#

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

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

如果在生成子进程时将 serialization 选项设置为 'advanced',则 message 参数可能包含 JSON 无法表示的数据。有关更多详细信息,请参阅 高级序列化

事件:'spawn'#

一旦子进程成功生成,就会触发 'spawn' 事件。 如果子进程未成功生成,则不会触发 'spawn' 事件,而是触发 'error' 事件。

如果触发,'spawn' 事件会发生在所有其他事件之前,并且在通过 stdoutstderr 接收到任何数据之前。

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

subprocess.channel#

  • <Object> 一个管道,表示与子进程的 IPC 通道。

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

subprocess.channel.ref()#

如果之前调用过 .unref(),则此方法使 IPC 通道保持父进程的事件循环运行。

subprocess.channel.unref()#

此方法使 IPC 通道不保持父进程的事件循环运行,并允许它在通道打开时完成。

subprocess.connected#

  • <boolean> 在调用 subprocess.disconnect() 之后设置为 false

subprocess.connected 属性指示是否仍可以从子进程发送和接收消息。 当 subprocess.connectedfalse 时,不再可能发送或接收消息。

subprocess.disconnect()#

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

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

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

subprocess.exitCode#

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

subprocess.kill([signal])#

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

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

grep.on('close', (code, signal) => {
  console.log(
    `child process terminated due to receipt of signal ${signal}`);
});

// Send SIGHUP to process.
grep.kill('SIGHUP');import { spawn } from 'node:child_process';
const grep = spawn('grep', ['ssh']);

grep.on('close', (code, signal) => {
  console.log(
    `child process terminated due to receipt of signal ${signal}`);
});

// Send SIGHUP to process.
grep.kill('SIGHUP');

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

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

有关参考,请参见 kill(2)

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

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

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(); // Does not terminate the Node.js process in the shell.
}, 2000);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(); // Does not terminate the Node.js process in the shell.
}, 2000);

subprocess[Symbol.dispose]()#

稳定性:1 - 实验性

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

subprocess.killed#

  • <boolean> 在使用 subprocess.kill() 成功向子进程发送信号后设置为 true

subprocess.killed 属性指示子进程是否成功从 subprocess.kill() 接收到信号。 killed 属性不指示子进程是否已终止。

subprocess.pid#

返回子进程的进程标识符 (PID)。 如果子进程由于错误而无法生成,则该值为 undefined 并且发出 error

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

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

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

subprocess.ref()#

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

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();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])#

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

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

例如,在父脚本中

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

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

// Causes the child to print: CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' });import { fork } from 'node:child_process';
const forkedProcess = fork(`${import.meta.dirname}/sub.js`);

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

// Causes the child to print: CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' });

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

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

// Causes the parent to print: 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 服务器对象的句柄给子进程,如下面的例子所示

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

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

// Open up the server object and send the handle.
const server = createServer();
server.on('connection', (socket) => {
  socket.end('handled by parent');
});
server.listen(1337, () => {
  subprocess.send('server', server);
});import { fork } from 'node:child_process';
import { createServer } from 'node:net';

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

// Open up the server object and send the handle.
const server = createServer();
server.on('connection', (socket) => {
  socket.end('handled by parent');
});
server.listen(1337, () => {
  subprocess.send('server', server);
});

子进程将会收到如下的服务器对象:

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 参数可以被用于传递一个套接字的句柄给子进程。下面的例子派生了两个子进程,每个子进程处理“normal”或“special”优先级的连接。

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

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

// Open up the server and send sockets to child. Use pauseOnConnect to prevent
// the sockets from being read before they are sent to the child process.
const server = createServer({ pauseOnConnect: true });
server.on('connection', (socket) => {

  // If this is special priority...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
    return;
  }
  // This is normal priority.
  normal.send('socket', socket);
});
server.listen(1337);import { fork } from 'node:child_process';
import { createServer } from 'node:net';

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

// Open up the server and send sockets to child. Use pauseOnConnect to prevent
// the sockets from being read before they are sent to the child process.
const server = createServer({ pauseOnConnect: true });
server.on('connection', (socket) => {

  // If this is special priority...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
    return;
  }
  // This is normal priority.
  normal.send('socket', socket);
});
server.listen(1337);

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

process.on('message', (m, socket) => {
  if (m === 'socket') {
    if (socket) {
      // Check that the client socket exists.
      // It is possible for the socket to be closed between the time it is
      // sent and the time it is received in the child process.
      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#

一个 Readable Stream,表示子进程的 stderr

如果子进程是以 stdio[2] 设置为除 'pipe' 之外的任何值而派生的,那么这将是 null

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

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

subprocess.stdin#

一个 Writable Stream,表示子进程的 stdin

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

如果子进程是以 stdio[0] 设置为除 'pipe' 之外的任何值而派生的,那么这将是 null

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

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

subprocess.stdio#

子进程管道的稀疏数组,对应于传递给 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

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, // Use parent's stdin for child.
    'pipe', // Pipe child's stdout to parent.
    fs.openSync('err.out', 'w'), // Direct child's stderr to a file.
  ],
});

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);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, // Use parent's stdin for child.
    'pipe', // Pipe child's stdout to parent.
    fs.openSync('err.out', 'w'), // Direct child's stderr to a file.
  ],
});

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#

一个 Readable Stream,表示子进程的 stdout

如果子进程是以 stdio[1] 设置为除 'pipe' 之外的任何值而派生的,那么这将是 null

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

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

const subprocess = spawn('ls');

subprocess.stdout.on('data', (data) => {
  console.log(`Received chunk ${data}`);
});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()#

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

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();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)的输出。 例如,尽管只有 4 个字符,console.log('中文测试') 也会将 13 个 UTF-8 编码的字节发送到 stdout

Shell 要求#

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

默认 Windows shell#

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

高级序列化#

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

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