Node.js child_process 模組提供四種衍生子進程的方式,讓 JavaScript 能在伺服器端執行 Shell 指令、啟動外部程式、建立管道通訊。本文詳細解析 exec、execFile、spawn、fork 四大 API 的適用場景、參數細節、錯誤處理與安全最佳實踐。

| API | 回傳 | 適用場景 | 特性 |
|---|---|---|---|
exec() | Promise/Callback (stdout/stderr 緩衝) | 簡單 Shell 指令、短期執行、需完整輸出 | 啟動 Shell 解析、緩衝區預設 1MB、支援管道/重導向 |
execFile() | Promise/Callback | 直接執行可執行檔 (無 Shell 解析) | 較輕量、避免 Shell 注入、需指定完整路徑 |
spawn() | ChildProcess (Stream) | 長期運行、大量輸出、串流處理、雙向通訊 | 不緩衝輸出、標準流為 Stream、支援自訂 stdio |
fork() | ChildProcess (IPC) | Node.js 子進程、多核並行、傳遞物件訊息 | 建立 IPC 通道、process.send()/message 事件、獨立 V8 實例 |
const { exec } = require('child_process');
const util = require('util');
const execPromise = util.promisify(exec);
// 基本用法 (Callback)
exec('pwd', (error, stdout, stderr) => {
if (error) { console.error(`exec error: ${error}`); return; }
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
});
// Promise/async-await 風格 (推薦)
async function runCmd() {
try {
const { stdout, stderr } = await execPromise('ls -la /tmp');
console.log(stdout);
} catch (err) {
console.error(`Command failed: ${err.message}`);
console.error(`stderr: ${err.stderr}`);
}
}
// 複雜 Shell 指令 (管道、重導向、變數)
await execPromise('cd /var/log && grep -i error *.log | wc -l');
// 選項控制
await execPromise('sleep 10', {
timeout: 5000, // 超時毫秒數,超時殺死進程
maxBuffer: 1024 * 1024, // stdout/stderr 最大緩衝 (預設 1MB)
cwd: '/home/user', // 工作目錄
env: { ...process.env, NODE_ENV: 'production' }, // 環境變數
shell: '/bin/bash' // 指定 Shell (預設 /bin/sh)
});
const { execFile } = require('child_process');
const util = require('util');
const execFilePromise = util.promisify(execFile);
// 執行腳本檔 (需 Shebang + 執行權限)
await execFilePromise('/opt/scripts/backup.sh', ['--full', '--dest=/backup']);
// 執行二進位程式帶參數
await execFilePromise('/usr/bin/docker', ['ps', '--format', 'json']);
// Windows 批次檔
await execFilePromise('cmd.exe', ['/c', 'D:\\scripts\\build.bat']);
// 選項
await execFilePromise('/path/to/app', ['arg1', 'arg2'], {
cwd: '/working/dir',
timeout: 30000,
windowsHide: true // Windows 隱藏視窗
});
const { spawn } = require('child_process');
// 即時處理標準輸出流
const find = spawn('find', ['/var/log', '-name', '*.log', '-mtime', '-7']);
let output = '';
find.stdout.on('data', (chunk) => {
output += chunk.toString();
// 即時處理每一行
chunk.toString().split('\n').forEach(line => {
if (line) console.log(`[FOUND] ${line}`);
});
});
find.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
find.on('close', (code) => {
console.log(`find exited with code ${code}`);
console.log(`Total output length: ${output.length}`);
});
// 雙向通訊範例:互動式程式 (如 python3 -i)
const python = spawn('python3', ['-i'], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
python.stdin.write("print('hello from node')\n");
python.stdin.end();
// 選項:'pipe' (預設), 'ignore', 'inherit', 'ipc', Stream, 整數 fd
const child = spawn('node', ['worker.js'], {
stdio: [
'pipe', // stdin -> 父進程可寫入
'pipe', // stdout -> 父進程可讀取
'inherit', // stderr -> 直接繼承父進程 stderr (終端機顯示)
'ipc' // fd 3: IPC 通道 (需 fork 或 node --enable-ipc)
]
});
// master.js
const { fork } = require('child_process');
const cpus = require('os').cpus().length;
for (let i = 0; i < cpus; i++) {
const worker = fork('./worker.js', [i]);
worker.on('message', (msg) => {
console.log(`Worker ${msg.workerId} finished: ${msg.result}`);
});
worker.send({ taskId: i, data: generateTaskData(i) });
}
// worker.js
process.on('message', (task) => {
const result = heavyComputation(task.data);
process.send({ workerId: task.taskId, result });
});
| 風險寫法 | 安全寫法 | 原理 |
|---|---|---|
exec('ls ' + userInput) | execFile('ls', [userInput]) | 繞過 Shell 解析,參數直接傳給 execve |
exec('cmd ' + arg) | exec('cmd', [arg]) (Node 18+) | 陣列形式自動跳脫 |
使用者輸入含 ; rm -rf / | 嚴格驗證/白名單/跳脫 | shell-escape 套件或手動正則 |
| 錯誤 | 原因 | 解決 |
|---|---|---|
ENOENT | 指令/路徑不存在 | 使用 which 或 command -v 驗證,指定完整路徑 |
EACCES | 權限不足 | chmod +x、檢查檔案系統 noexec 掛載選項 |
E2BIG | 參數過長 | 改用 spawn 串流、或寫入暫存檔 |
maxBuffer exceeded | 輸出超過 1MB | 增大 maxBuffer 或改用 spawn |
| 殭屍進程 | 未監聽 close/exit | 必須處理退出事件,或使用 detached: true 獨立進程組 |
掌握 child_process 四大 API 的適用邊界、串流處理模式、安全防注入原則,能讓 Node.js 安全、高效地作為系統自動化、DevOps 工具、微服務閘道的編排中樞。