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(真正的多线程)。