在线工具
网站地图    收藏    合作   
<

快捷菜单 返回顶部

node中文API-child_process子进程

child_process 子进程#

中英对照

稳定性: 2 - 稳定

源代码: lib/child_process.js

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.error(`stderr: ${data}`);
});

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

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

如果在 options 对象中有 options.env.PATH 环境变量,则使用其执行命令查找。 否则,使用 process.env.PATH

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

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 上运行时,.bat.cmd 文件可以使用具有 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.error(data.toString());
});

bat.on('exit', (code) => {
  console.log(`Child exited with code ${code}`);
});
// 或者...
const { exec, spawn } = 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,缓冲任何生成的输出。 传给执行函数的 command 字符串由 shell 直接处理,特殊字符(因 shell 而异)需要进行相应处理:

const { exec } = require('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 参数将包含子进程的标准输出和标准错误的输出。 默认情况下,Node.js 会将输出解码为 UTF-8 并将字符串传给回调。 encoding 选项可用于指定用于解码标准输出和标准错误的输出的字符编码。 如果 encoding'buffer' 或无法识别的字符编码,则 Buffer 对象将被传给回调。

const { exec } = require('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() 版本被调用,则其将为具有 stdoutstderr 属性的 Object 返回 Promise。 返回的 ChildProcess 实例作为 child 属性附加到 Promise。 如果出现错误(包括任何导致退出码不是 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.error('stderr:', stderr);
}
lsExample();

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

const { exec } = require('child_process');
const controller = new AbortController();
const { signal } = controller;
const child = exec('grep ssh', { signal }, (error) => {
  console.log(error); // 一个 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> 标准输出或标准错误上允许的最大数据量(以字节为单位)。 如果超过,则子进程将终止并截断任何输出。 请参阅 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> 允许使用中止信号中止子进程。
  • callback <Function> 进程终止时使用输出调用。
  • 返回: <ChildProcess>

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 参数将包含子进程的标准输出和标准错误的输出。 默认情况下,Node.js 会将输出解码为 UTF-8 并将字符串传给回调。 encoding 选项可用于指定用于解码标准输出和标准错误的输出的字符编码。 如果 encoding'buffer' 或无法识别的字符编码,则 Buffer 对象将被传给回调。

如果此方法作为其 util.promisify() 版本被调用,则其将为具有 stdoutstderr 属性的 Object 返回 Promise。 返回的 ChildProcess 实例作为 child 属性附加到 Promise。 如果出现错误(包括任何导致退出码不是 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 元字符的输入都可用于触发任意命令执行。

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

const { execFile } = require('child_process');
const controller = new AbortController();
const { signal } = controller;
const child = execFile('node', ['--version'], { signal }, (error) => {
  console.log(error); // 一个 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> 允许使用中止信号关闭子进程。
    • killSignal <string> | <integer> 当衍生的进程将被超时或中止信号杀死时要使用的信号值。 默认值: 'SIGTERM'
    • silent <boolean> 如果为 true,则子进程的标准输入、标准输出和标准错误将通过管道传输到父进程,否则它们将从父进程继承,有关详细信息,请参阅 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 标识的文件描述符与父进程通信。

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

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

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

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`);
  }, 1_000);
} else {
  const { fork } = require('child_process');
  const controller = new AbortController();
  const { signal } = controller;
  const child = fork(__filename, ['child'], { signal });
  child.on('error', (err) => {
    // 如果控制器中止,则这将在 err 为 AbortError 的情况下被调用
  });
  controller.abort(); // 停止子进程
}

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> 子进程的标准输入输出配置(参见 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> 允许使用中止信号中止子进程。
    • timeout <number> 允许进程运行的最长时间(以毫秒为单位)。 默认值: undefined
    • killSignal <string> | <integer> 当衍生的进程将被超时或中止信号杀死时要使用的信号值。 默认值: 'SIGTERM'
  • 返回: <ChildProcess>

child_process.spawn() 方法使用给定的 commandargs 中的命令行参数衍生新进程。 如果省略,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('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('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('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('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#

中英对照

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

在非 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#

中英对照