4 个方法
| 方法 | 用途 |
|---|---|
spawn |
跑外部命令,流式输出(推荐) |
exec |
跑外部命令,buffered(小输出) |
execFile |
同 exec 但不走 shell(更安全) |
fork |
跑另一个 Node 脚本 + IPC 通信 |
spawn:流式跑命令
import { spawn } from 'child_process';
const ls = spawn('ls', ['-lh', '/var/log']);
ls.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', data => {
console.error(`stderr: ${data}`);
});
ls.on('close', code => {
console.log(`exited with code ${code}`);
});
适合:大输出 / 长时间运行 / 需要持续看输出。
exec:一次性拿结果
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
const { stdout, stderr } = await execAsync('ls -lh /var/log');
console.log(stdout);
或:
exec('git log --oneline -5', (err, stdout, stderr) => {
if (err) return console.error(err);
console.log(stdout);
});
⚠ exec 有缓冲区限制(默认 1 MB)—— 超过截断 + 报错。大输出用 spawn。
execFile:安全版 exec
import { execFile } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
// 不走 shell → 不会被参数注入
const { stdout } = await execFileAsync('node', ['--version']);
⚠ exec 通过 shell 跑 = 用户输入参数有注入风险:
// ❌ 危险(用户传 "; rm -rf /" 就完了)
exec(`grep ${userInput} log.txt`);
// ✓ 安全
execFile('grep', [userInput, 'log.txt']);
优先用 execFile / spawn 的参数数组形式。
处理大输出(stdout pipe)
const proc = spawn('curl', ['-s', 'https://example.com']);
let output = '';
proc.stdout.setEncoding('utf8');
for await (const chunk of proc.stdout) {
output += chunk;
}
await new Promise(resolve => proc.on('close', resolve));
console.log(output);
给子进程发输入
const py = spawn('python3', ['-c', 'import sys; print(sys.stdin.read().upper())']);
py.stdin.write('hello\n');
py.stdin.end();
py.stdout.on('data', d => console.log(d.toString()));
fork:跑另一个 Node 脚本
// parent.js
import { fork } from 'child_process';
const child = fork('./worker.js');
child.send({ task: 'process', data: [1, 2, 3] });
child.on('message', msg => {
console.log('result:', msg);
});
// worker.js
process.on('message', msg => {
const result = msg.data.map(x => x * 2);
process.send(result);
});
fork 自动建立IPC 通道(通过 .send / 'message' 事件)。
适合:CPU 密集任务想用多核(替代方案:worker_threads 更轻)。
杀子进程
const child = spawn('long-task');
setTimeout(() => {
child.kill('SIGTERM'); // 默认 SIGTERM
// 不响应可以再 child.kill('SIGKILL')
}, 5000);
child.on('exit', (code, signal) => {
console.log(`exit code=${code} signal=${signal}`);
});
流水线:连接两命令
import { spawn } from 'child_process';
const grep = spawn('grep', ['ERROR'], { stdio: ['pipe', 'pipe', 'inherit'] });
const cat = spawn('cat', ['/var/log/syslog'], { stdio: ['inherit', 'pipe', 'inherit'] });
cat.stdout.pipe(grep.stdin);
grep.stdout.on('data', d => process.stdout.write(d));
实战中用 shell 的管道更简单:
exec("cat /var/log/syslog | grep ERROR");
完整示例:脚本运行器
import { spawn } from 'child_process';
async function run(cmd, args = []) {
return new Promise((resolve, reject) => {
const proc = spawn(cmd, args, { stdio: 'inherit' }); // 直接显示在终端
proc.on('close', code => {
if (code === 0) resolve();
else reject(new Error(`${cmd} exited with ${code}`));
});
proc.on('error', reject);
});
}
await run('npm', ['install']);
await run('npm', ['test']);
await run('npm', ['run', 'build']);
坑
exec默认 shell = 注入风险——用户输入永远用execFile- 大输出用
exec→ 超 maxBuffer 报错;用 spawn - 子进程没 stdin 时挂起在等输入——加
stdio: 'ignore' - Windows 上有些命令找不到——
shell: true或加.exe
下一篇:worker_threads(真正的多线程)。