Node.js 子进程:child_process

383 阅读2分钟

概述

Node.js 基于单线程事件循环模型,——当遇到 CPU 密集型任务(如大规模数学计算、图像/视频处理、文件压缩等)时,单个线程会被阻塞,导致整个应用停滞不前,无法处理其他请求。

child_process 模块正是 Node.js 为我们提供的,用于跳出单线程限制、真正利用多核 CPU 性能。它允许你创建并管理子进程,从而执行系统命令、运行其他脚本或可执行文件,实现并行处理。

简单来说,child_process 能够在 Node.js 程序中“开小号”,让多个任务同时进行,极大地提升了程序的性能。

核心 API

child_process 模块提供了如下几个主要方法来创建子进,适用于不同的场景。

1. exec:执行 Shell 命令

exec 方法用于执行一个 Shell 命令。它会启动一个 Shell(如 /bin/sh on Unix,cmd.exe on Windows)来解析命令,这意味着你可以使用管道 |、重定向 > 等 Shell 特性。

它的特点是缓冲输出,即会等待命令完全执行完毕,然后将所有输出一次性返回给你。

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

// 执行 git status 命令
exec('git status', (error, stdout, stderr) => {
  if (error) {
    console.error(`执行出错: ${error.message}`);
    return;
  }
  if (stderr) {
    console.error(`标准错误: ${stderr}`);
    return;
  }
  console.log(`标准输出: ${stdout}`);
});

适用场景

  • 执行简单的、输出量不大的 Shell 命令,并获取最终结果。例如,获取项目 git 状态、检查磁盘空间 (df -h) 等。

2. spawn:流式处理

spawn 它不启动一个 Shell,而是直接衍生一个新的进程来执行指定的命令。它通过流(Stream)  的方式返回子进程的 stdout 和 stderr

这意味着你可以实时地接收到输出数据块,而不是等到命令结束。这对于处理大量数据(如处理大文件、长日志)至关重要,因为内存占用小,效率高。

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

// 使用 spawn 执行 find 命令,查找所有 .js 文件
const child = spawn('find', ['.', '-name', '*.js']);

// 实时接收数据
child.stdout.on('data', (data) => {
  console.log(`找到文件: ${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`错误: ${data}`);
});

// 监听进程结束
child.on('close', (code) => {
  console.log(`子进程退出,退出码: ${code}`);
});

适用场景

  • 处理大量输出:如 npm installdocker build 等需要实时查看进度的命令。

3. execFile:二进制执行

execFile 与 exec 类似,但它不启动 Shell。它直接执行一个可执行文件。这使得它比 exec 效率稍高,而且更安全(因为它避免了潜在的 Shell 注入攻击)。但它不能使用 Shell 的原语,如通配符 * 或管道 |

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

// 直接执行 node 可执行文件,并传递参数
execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error;
  }
  console.log(stdout); // v18.x.x
});

适用场景:运行已知的、不需要 Shell 特性的二进制文件或脚本,追求更高的安全性和效率。

总结

方法核心特点输出处理典型使用场景
exec使用 Shell缓冲简单的 Shell 命令,输出量小
spawn不使用 Shell,高效流式 (Stream)需要实时输出、处理大量数据
execFile不使用 Shell,安全缓冲执行已知的二进制可执行文件

上面的几个方法在项目工程化开发插件、提供开发工具、规范、文件输出、脚手架改造的时候比较常用。