nodejs中child_process模块的正确使用

409 阅读2分钟

在Node.js中,child_process模块用于创建和管理子进程,允许执行外部命令、脚本或与其他进程通信。以下是正确使用该模块的步骤和示例:

1. 选择合适的子进程创建方法

  • exec: 执行简单命令,适合预期输出较小的场景,返回缓冲结果。
  • execFile: 类似exec,但直接执行文件,不通过shell,更安全。
  • spawn: 处理大量数据或流式输入输出,适合实时处理。
  • fork: 创建Node.js子进程,通过IPC通信,适合多核计算。

2. 避免命令注入

当参数包含用户输入时,使用execFilespawn并传递参数数组,避免直接拼接字符串。

示例(安全执行命令):

const { execFile } = require('child_process');
execFile('ls', ['-l', '.'], (error, stdout) => {
  if (error) throw error;
  console.log(stdout);
});

3. 处理流和输出

使用spawn时,通过事件监听实时数据流。

示例(实时输出处理):

const { spawn } = require('child_process');
const child = spawn('ls', ['-l']);

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

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

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

4. 错误和退出处理

监听errorexit事件,确保资源释放。

示例(错误处理):

const { exec } = require('child_process');
exec('invalid_command', (error, stdout, stderr) => {
  if (error) {
    console.error(`错误: ${error.message}`);
    return;
  }
  if (stderr) {
    console.error(`STDERR: ${stderr}`);
  }
  console.log(`STDOUT: ${stdout}`);
});

5. 超时控制

设置超时避免进程挂起。

示例(超时终止):

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

const timeout = setTimeout(() => {
  child.kill();
  console.error('命令超时');
}, 5000);

child.on('exit', () => {
  clearTimeout(timeout);
});

6. 使用IPC通信(fork)

父子进程通过sendmessage事件通信。

父进程:

const { fork } = require('child_process');
const child = fork('child.js');

child.on('message', (msg) => {
  console.log('父进程收到:', msg);
});

child.send({ parent: '数据' });

子进程(child.js):

process.on('message', (msg) => {
  console.log('子进程收到:', msg);
  process.send({ child: '响应' });
});

7. 跨平台兼容性

处理平台差异,如命令选择(ls vs dir)。

示例:

const command = process.platform === 'win32' ? 'dir' : 'ls';
const args = process.platform === 'win32' ? [] : ['-l'];
spawn(command, args);

8. 使用Promise封装

利用util.promisify简化异步操作。

示例(async/await):

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

async function run() {
  try {
    const { stdout } = await exec('ls -l');
    console.log(stdout);
  } catch (error) {
    console.error(error);
  }
}

run();

9. 资源管理

  • 确保子进程退出后释放资源。
  • 控制并发进程数量,避免资源耗尽。

总结

正确使用child_process需注意方法选择、安全性、流处理、错误控制和资源管理。根据场景选择合适API,妥善处理输入输出,并始终验证和清理用户输入,以确保应用安全和高效。