在Node.js中,child_process模块用于创建和管理子进程,允许执行外部命令、脚本或与其他进程通信。以下是正确使用该模块的步骤和示例:
1. 选择合适的子进程创建方法
- exec: 执行简单命令,适合预期输出较小的场景,返回缓冲结果。
- execFile: 类似
exec,但直接执行文件,不通过shell,更安全。 - spawn: 处理大量数据或流式输入输出,适合实时处理。
- fork: 创建Node.js子进程,通过IPC通信,适合多核计算。
2. 避免命令注入
当参数包含用户输入时,使用execFile或spawn并传递参数数组,避免直接拼接字符串。
示例(安全执行命令):
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. 错误和退出处理
监听error和exit事件,确保资源释放。
示例(错误处理):
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)
父子进程通过send和message事件通信。
父进程:
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,妥善处理输入输出,并始终验证和清理用户输入,以确保应用安全和高效。