Node.js Child Process 子程序:平行處理與外部指令執行

Node.js 雖然採用單執行緒 (Single-threaded) 的架構,但這並不代表它不能處理複雜的任務。透過 Child Process 模組,我們可以直接調用作業系統的資源來執行外部指令、運行腳本或進行多進程通訊,從而突破單核的限制。

執行簡單指令:exec 與 execFile

這是最常用的兩種執行方式,但它們在安全與用法上有細微差別:

1. exec(command, callback)

這是在一個 殼層 (Shell) 中執行指令。這意味著你可以使用 | (管線) 或 * (萬用字元)。

  • 缺點:如果 command 包含來自使用者的輸入,容易遭受 Shell Injection (殼層指令注入) 攻擊。
const { exec } = require('child_process');

exec('ls -lh', (err, stdout, stderr) => {
  if (err) return console.error('執行失敗:', err);
  console.log('檔案清單:\n', stdout);
});

2. execFile(file, args, callback)

不啟動 Shell,而是直接執行執行檔。參數以陣列形式傳入。

  • 優點:效能更好且更安全。推薦在不需要 Shell 特性時使用。

處理大量數據:spawn

如果你要執行會產生海量數據的任務(例如影片轉檔 ffmpeg 或 ping 回應),exec 的 200KB 預設緩衝區會爆掉。此時必須使用 串流形式spawn

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

const ping = spawn('ping', ['google.com', '-c', '4']);

ping.stdout.on('data', (data) => {
  console.log(`數據回報:${data}`);
});

ping.on('close', (code) => {
  console.log(`子程序結束,結束代碼:${code}`);
});

Node 進階通訊:fork

fork 是專為兩個 Node.js 程序設計的通訊工具。它會建立一個內建的 IPC (Inter-Process Communication) 頻道。

// parent.js
const { fork } = require('child_process');
const worker = fork('worker.js');

worker.send({ task: 'compute' }); // 發送物件給子程序
worker.on('message', (result) => {
  console.log('收到計算結果:', result);
});

應該選擇哪一個?

方法核心特性回傳方式適用場景
exec在 Shell 中執行Callback (緩衝)簡單的一行腳本指令
execFile直接執行檔案Callback (緩衝)安全性要求高的外部程式調用
spawn串流式傳輸Stream大量數據處理(影音、Log)
forkNode 專屬通訊IPC將耗時計算移到另一個進程
重要提示:子程序是獨立的系統資源,開啟過多進程會劇烈消耗系統記憶體。如果你是為了優化 CPU 密集型的純 JS 計算(如圖片處理),現在更推薦使用 Worker Threads

總結

  1. 追求安全與效能?首選 execFile
  2. 處理大數據?務必用 spawn
  3. 需要兩個 Node 程序互傳物件?請用 fork