NodeJs 子进程child_process

官方文档

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

1. spawn

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

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

  • 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>

对于其它的方法,参数类似

 1 const ps = child.spawn('ps', ['ax']);
 2 const grep = child.spawn('grep', ['ssh']);
 3 
 4 ps.stdout.on('data', (data) => {
 5     grep.stdin.write(data);
 6 });
 7 
 8 ps.stderr.on('data', (data) => {
 9     console.error(`ps stderr: ${data}`);
10 });
11 
12 ps.on('close', (code) => {
13     if (code !== 0) {
14         console.log(`ps process exited with code ${code}`);
15     }
16     grep.stdin.end();
17 });
18 
19 grep.stdout.on('data', (data) => {
20     console.log(data.toString());
21 });
22 
23 grep.stderr.on('data', (data) => {
24     console.error(`grep stderr: ${data}`);
25 });
26 
27 grep.on('close', (code) => {
28     if (code !== 0) {
29         console.log(`grep process exited with code ${code}`);
30     }
31 }); 

2. exec

和spawn功能类似,用于另开进程启动shell命令。

child_process.exec() 不替换现有进程,而是使用 shell 来执行命令。

举例:让其用ts-node直接运行typescript代码

1 import * as child from 'child_process';
2 
3 child.exec('ts-node child.ts 1', ((error, stdout, stderr) => {
4     if (error) {
5         console.error(error);
6         return;
7     }
8     console.log(stdout);
9 }));

3. execfile

和exec类似,不同之处在于它默认不衍生 shell。 而是,指定的可执行文件 file 直接作为新进程衍生,使其比 child_process.exec() 略有效率。

有args参数可以传类型为Array[string]的参数

1 import * as child from 'child_process';
2 
3 child.execFile('ts-node', ['child.ts'], ((error, stdout, stderr) => {
4     if (error) {
5         console.error(error);
6         return;
7     }
8     console.log(stdout);
9 }));

4. fork

用于另开进程执行javascript脚本,直接传js路径

1 import * as child from 'child_process';
2 
3 const c1 = child.fork('child.js', ['2'], {
4     silent: false,
5 });
6 
7 const c2 = child.fork('child.js',['3'], {
8     silent: false,
9 });

5. sync

每个方法都有对应的sync版本,nodejs执行时会阻塞直至子进程完全退出

6. 事件

'close' 事件

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

在进程已结束并且子进程的标准输入输出流已关闭之后,则触发 'close' 事件。 这与 'exit' 事件不同,因为多个进程可能共享相同的标准输入输出流。 'close' 事件将始终在 'exit''error'(如果子进程衍生失败)已经触发之后触发。

const { spawn } = require('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' 事件在以下情况下触发:

  1. 无法衍生该进程,或
  2. 进程无法终止,或
  3. 向子进程发送消息失败。

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

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

'exit' 事件

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

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

'exit' 事件被触发时,子进程标准输入输出流可能仍处于打开状态。

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 } 时,此注意事项也适用。