子程序#

穩定度:2 - 穩定

node:child_process 模組提供了以類似但不同於 popen(3) 的方式衍生子程序的能力。此功能主要是由 child_process.spawn() 函式提供的。

const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});
import { spawn } from 'node:child_process';
import { once } from 'node:events';
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

const [code] = await once(ls, 'close');
console.log(`child process exited with code ${code}`);

預設情況下,父 Node.js 程序與衍生的子程序之間會建立 stdinstdoutstderr 的管道。這些管道具有限制(且視平台而定)的容量。如果子程序寫入 stdout 的量超過該限制且未擷取輸出,則子程序將會阻塞,等待管道緩衝區接受更多資料。這與 shell 中管道的行為完全相同。如果不打算取用輸出,請使用 { stdio: 'ignore' } 選項。

如果 options 物件中包含 env,則會使用 options.env.PATH 環境變數執行指令搜尋。否則,將使用 process.env.PATH。如果設定了 options.env 但沒有 PATH,在 Unix 上會於預設搜尋路徑 /usr/bin:/bin 執行搜尋(請參閱作業系統的 execvpe/execvp 手冊),在 Windows 上則會使用目前程序的環境變數 PATH

在 Windows 上,環境變數不區分大小寫。Node.js 會按字典順序對 env 的鍵進行排序,並使用第一個不區分大小寫相符的鍵。只有(按字典順序)第一個項目會傳遞給子程序。當傳遞給 env 選項的物件中包含同一個鍵的多個變體(例如 PATHPath)時,這可能會在 Windows 上導致問題。

child_process.spawn() 方法會非同步衍生子程序,而不會阻塞 Node.js 事件迴圈。child_process.spawnSync() 函式則是以同步方式提供相同的功能,會阻塞事件迴圈直到衍生的程序結束或被終止為止。

為了方便起見,node:child_process 模組提供了幾種 child_process.spawn()child_process.spawnSync() 的同步與非同步替代方案。這些替代方案中的每一種都是建構在 child_process.spawn()child_process.spawnSync() 之上的。

對於某些使用案例(例如自動化 shell 指令稿),同步對應方法可能更為方便。然而,在許多情況下,同步方法會因為在衍生程序完成期間停頓事件迴圈,而對效能產生顯著影響。

非同步程序建立#

child_process.spawn()child_process.fork()child_process.exec()child_process.execFile() 方法都遵循其他 Node.js API 特有的慣用非同步程式設計模式。

這些方法中的每一種都會回傳一個 ChildProcess 執行個體。這些物件實作了 Node.js EventEmitter API,允許父程序註冊接聽程式函式,並在子程序的生命週期中發生特定事件時呼叫這些函式。

child_process.exec()child_process.execFile() 方法另外允許指定一個選用的 callback 函式,該函式會在子程序終止時被呼叫。

在 Windows 上衍生 .bat.cmd 檔案#

child_process.exec()child_process.execFile() 之間區別的重要性會依平台而異。在 Unix 類型的作業系統(Unix、Linux、macOS)上,child_process.execFile() 可能更有效率,因為它預設不會衍生 shell。然而,在 Windows 上,.bat.cmd 檔案在沒有終端機的情況下本身是無法執行的,因此無法使用 child_process.execFile() 啟動。在 Windows 上執行時,可以透過以下方式呼叫 .bat.cmd 檔案:

在任何情況下,如果指令稿檔名包含空格,則需要加上引號。

const { exec, spawn } = require('node:child_process');

exec('my.bat', (err, stdout, stderr) => { /* ... */ });

// Or, spawning cmd.exe directly:
const bat = spawn('cmd.exe', ['/c', 'my.bat']);

// If the script filename contains spaces, it needs to be quoted
exec('"my script.cmd" a b', (err, stdout, stderr) => { /* ... */ });
import { exec, spawn } from 'node:child_process';

exec('my.bat', (err, stdout, stderr) => { /* ... */ });

// Or, spawning cmd.exe directly:
const bat = spawn('cmd.exe', ['/c', 'my.bat']);

// If the script filename contains spaces, it needs to be quoted
exec('"my script.cmd" a b', (err, stdout, stderr) => { /* ... */ });

child_process.exec(command[, options][, callback])#

衍生一個 shell 並在該 shell 中執行 command,並緩衝任何產生的輸出。傳遞給 exec 函式的 command 字串會直接由 shell 處理,且需要相應地處理特殊字元(依 shell 而異)。

const { exec } = require('node:child_process');

exec('"/path/to/test file/test.sh" arg1 arg2');
// Double quotes are used so that the space in the path is not interpreted as
// a delimiter of multiple arguments.

exec('echo "The \\$HOME variable is $HOME"');
// The $HOME variable is escaped in the first instance, but not in the second.
import { exec } from 'node:child_process';

exec('"/path/to/test file/test.sh" arg1 arg2');
// Double quotes are used so that the space in the path is not interpreted as
// a delimiter of multiple arguments.

exec('echo "The \\$HOME variable is $HOME"');
// The $HOME variable is escaped in the first instance, but not in the second.

絕對不要將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

如果提供了 callback 函式,它將與引數 (error, stdout, stderr) 一起被呼叫。成功時,error 將為 null。出錯時,error 將是 Error 的執行個體。error.code 屬性將是程序的結束代碼。按照慣例,除 0 以外的任何結束代碼都表示錯誤。error.signal 將是終止程序的訊號。

傳遞給回呼的 stdoutstderr 引數將包含子程序的 stdout 與 stderr 輸出。預設情況下,Node.js 將以 UTF-8 解碼輸出並將字串傳遞給回呼。encoding 選項可用於指定解碼 stdout 與 stderr 輸出所使用的字元編碼。如果 encoding'buffer' 或無法辨識的字元編碼,則 Buffer 物件將被傳遞給回呼。

const { exec } = require('node:child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});
import { exec } from 'node:child_process';
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});

如果 timeout 大於 0,則當子程序執行超過 timeout 毫秒時,父程序將發送 killSignal 屬性標識的訊號(預設為 'SIGTERM')。

exec(3) POSIX 系統呼叫不同,child_process.exec() 不會替換現有程序,而是使用 shell 來執行指令。

如果此方法以其 util.promisify() 版本呼叫,則它會回傳一個 Promise,其結果為具有 stdoutstderr 屬性的 Object。回傳的 ChildProcess 執行個體會作為 child 屬性附加到 Promise 上。如果發生錯誤(包括導致結束代碼不為 0 的任何錯誤),則會回傳 rejected promise,其中包含回呼中給出的相同 error 物件,但具有兩個額外的屬性:stdoutstderr

const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.error('stderr:', stderr);
}
lsExample();
import { promisify } from 'node:util';
import child_process from 'node:child_process';
const exec = promisify(child_process.exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.error('stderr:', stderr);
}
lsExample();

如果啟用了 signal 選項,在相應的 AbortController 上呼叫 .abort() 類似於在子程序上呼叫 .kill(),不同之處在於傳遞給回呼的錯誤將是 AbortError

const { exec } = require('node:child_process');
const controller = new AbortController();
const { signal } = controller;
const child = exec('grep ssh', { signal }, (error) => {
  console.error(error); // an AbortError
});
controller.abort();
import { exec } from 'node:child_process';
const controller = new AbortController();
const { signal } = controller;
const child = exec('grep ssh', { signal }, (error) => {
  console.error(error); // an AbortError
});
controller.abort();

child_process.execFile(file[, args][, options][, callback])#

  • file <string> 要執行的執行檔名稱或路徑。
  • args <string[]> 字串引數列表。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • encoding <string> 預設值: 'utf8'
    • timeout <number> 預設值: 0
    • maxBuffer <number> stdout 或 stderr 允許的最大資料量(以位元組為單位)。如果超過,子程序將被終止,且任何輸出都會被截斷。請參閱 maxBuffer 與 Unicode 中的警語。預設值: 1024 * 1024
    • killSignal <string> | <integer> 預設值: 'SIGTERM'
    • uid <number> 設定程序的用戶識別碼(請參閱 setuid(2))。
    • gid <number> 設定程序的群組識別碼(請參閱 setgid(2))。
    • windowsHide <boolean> 隱藏通常會在 Windows 系統上建立的子程序主控台視窗。 預設值: false
    • windowsVerbatimArguments <boolean> 在 Windows 上不對引數進行引號或逸出處理。在 Unix 上會被忽略。 預設值: false
    • shell <boolean> | <string> 如果為 true,則在 shell 中執行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以將不同的 shell 指定為字串。請參閱 Shell 需求預設 Windows shell預設值: false(不使用 shell)。
    • signal <AbortSignal> 允許使用 AbortSignal 中止子程序。
  • callback <Function> 當程序終止時,連同輸出一起呼叫。
  • 回傳:<ChildProcess>

child_process.execFile() 函式類似於 child_process.exec(),不同之處在於它預設不會衍生 shell。相反地,指定的執行檔 file 會直接作為新程序衍生,使其比 child_process.exec() 略微有效率。

支援與 child_process.exec() 相同的選項。由於不衍生 shell,因此不支援 I/O 重導與檔案萬用字元(file globbing)等行為。

const { execFile } = require('node:child_process');
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error;
  }
  console.log(stdout);
});
import { execFile } from 'node:child_process';
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error;
  }
  console.log(stdout);
});

傳遞給回呼的 stdoutstderr 引數將包含子程序的 stdout 與 stderr 輸出。預設情況下,Node.js 將以 UTF-8 解碼輸出並將字串傳遞給回呼。encoding 選項可用於指定解碼 stdout 與 stderr 輸出所使用的字元編碼。如果 encoding'buffer' 或無法辨識的字元編碼,則 Buffer 物件將被傳遞給回呼。

如果此方法以其 util.promisify() 版本呼叫,則它會回傳一個 Promise,其結果為具有 stdoutstderr 屬性的 Object。回傳的 ChildProcess 執行個體會作為 child 屬性附加到 Promise 上。如果發生錯誤(包括導致結束代碼不為 0 的任何錯誤),則會回傳 rejected promise,其中包含回呼中給出的相同 error 物件,但具有兩個額外的屬性:stdoutstderr

const util = require('node:util');
const execFile = util.promisify(require('node:child_process').execFile);
async function getVersion() {
  const { stdout } = await execFile('node', ['--version']);
  console.log(stdout);
}
getVersion();
import { promisify } from 'node:util';
import child_process from 'node:child_process';
const execFile = promisify(child_process.execFile);
async function getVersion() {
  const { stdout } = await execFile('node', ['--version']);
  console.log(stdout);
}
getVersion();

如果啟用了 shell 選項,請勿將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

如果啟用了 signal 選項,在相應的 AbortController 上呼叫 .abort() 類似於在子程序上呼叫 .kill(),不同之處在於傳遞給回呼的錯誤將是 AbortError

const { execFile } = require('node:child_process');
const controller = new AbortController();
const { signal } = controller;
const child = execFile('node', ['--version'], { signal }, (error) => {
  console.error(error); // an AbortError
});
controller.abort();
import { execFile } from 'node:child_process';
const controller = new AbortController();
const { signal } = controller;
const child = execFile('node', ['--version'], { signal }, (error) => {
  console.error(error); // an AbortError
});
controller.abort();

child_process.fork(modulePath[, args][, options])#

  • modulePath <string> | <URL> 在子程序中執行的模組。
  • args <string[]> 字串引數列表。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • detached <boolean> 準備子程序以獨立於其父程序執行。具體行為取決於平台(請參閱 options.detached)。
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • execPath <string> 用於建立子程序的執行檔。
    • execArgv <string[]> 傳遞給執行檔的字串引數列表。 預設值: process.execArgv
    • gid <number> 設定程序的群組識別碼(請參閱 setgid(2))。
    • serialization <string> 指定用於在程序之間傳送訊息的序列化類型。可能的值為 'json''advanced'。請參閱 進階序列化 以取得更多詳細資訊。預設值: 'json'
    • signal <AbortSignal> 允許使用 AbortSignal 關閉子程序。
    • killSignal <string> | <integer> 當衍生程序因逾時或中止訊號而被終止時使用的訊號值。 預設值: 'SIGTERM'
    • silent <boolean> 如果為 true,則子程序的 stdin、stdout 與 stderr 將透過管道傳輸到父程序,否則它們將繼承自父程序,請參閱 child_process.spawn()stdio 中的 'pipe''inherit' 選項以取得更多詳細資訊。預設值: false
    • stdio <Array> | <string> 請參閱 child_process.spawn()stdio。提供此選項時,它會覆蓋 silent。如果使用陣列變體,它必須恰好包含一個值為 'ipc' 的項目,否則會擲回錯誤。例如 [0, 1, 2, 'ipc']
    • uid <number> 設定程序的用戶識別碼(請參閱 setuid(2))。
    • windowsVerbatimArguments <boolean> 在 Windows 上不對引數進行引號或逸出處理。在 Unix 上會被忽略。 預設值: false
    • timeout <number> 程序允許執行的最大時間(以毫秒為單位)。 預設值: undefined
  • 回傳:<ChildProcess>

child_process.fork() 方法是 child_process.spawn() 的一個特殊案例,專門用於衍生新的 Node.js 程序。與 child_process.spawn() 一樣,它會回傳一個 ChildProcess 物件。回傳的 ChildProcess 將具有內建的額外通訊頻道,允許在父子程序之間來回傳遞訊息。有關詳細資訊,請參閱 subprocess.send()

請記住,衍生的 Node.js 子程序與父程序是相互獨立的,唯一的例外是兩者之間建立的 IPC 通訊頻道。每個程序都有自己的記憶體,以及自己的 V8 執行個體。由於需要額外的資源配置,因此不建議衍生大量的 Node.js 子程序。

預設情況下,child_process.fork() 將使用父程序的 process.execPath 衍生新的 Node.js 執行個體。options 物件中的 execPath 屬性允許使用替代的執行路徑。

使用自訂 execPath 啟動的 Node.js 程序,將使用在子程序上透過環境變數 NODE_CHANNEL_FD 識別的檔案描述子 (fd) 與父程序通訊。

fork(2) POSIX 系統呼叫不同,child_process.fork() 不會複製目前程序。

child_process.spawn() 中可用的 shell 選項不被 child_process.fork() 支援,如果設定則會被忽略。

如果啟用了 signal 選項,在相應的 AbortController 上呼叫 .abort() 類似於在子程序上呼叫 .kill(),不同之處在於傳遞給回呼的錯誤將是 AbortError

const { fork } = require('node:child_process');
const process = require('node:process');

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`);
  }, 1_000);
} else {
  const controller = new AbortController();
  const { signal } = controller;
  const child = fork(__filename, ['child'], { signal });
  child.on('error', (err) => {
    // This will be called with err being an AbortError if the controller aborts
  });
  controller.abort(); // Stops the child process
}
import { fork } from 'node:child_process';
import process from 'node:process';

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`);
  }, 1_000);
} else {
  const controller = new AbortController();
  const { signal } = controller;
  const child = fork(import.meta.url, ['child'], { signal });
  child.on('error', (err) => {
    // This will be called with err being an AbortError if the controller aborts
  });
  controller.abort(); // Stops the child process
}

child_process.spawn(command[, args][, options])#

  • command <string> 要執行的指令。
  • args <string[]> 字串引數列表。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • argv0 <string> 明確設定傳送至子程序的 argv[0] 值。如果未指定,則會被設定為 command
    • stdio <Array> | <string> 子程序的 stdio 配置(請參閱 options.stdio)。
    • detached <boolean> 準備子程序以獨立於其父程序執行。具體行為取決於平台(請參閱 options.detached)。
    • uid <number> 設定程序的用戶識別碼(請參閱 setuid(2))。
    • gid <number> 設定程序的群組識別碼(請參閱 setgid(2))。
    • serialization <string> 指定用於在程序之間傳送訊息的序列化類型。可能的值為 'json''advanced'。請參閱 進階序列化 以取得更多詳細資訊。預設值: 'json'
    • shell <boolean> | <string> 如果為 true,則在 shell 中執行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以將不同的 shell 指定為字串。請參閱 Shell 需求預設 Windows shell預設值: false(不使用 shell)。
    • windowsVerbatimArguments <boolean> 在 Windows 上不對引數進行引號或逸出處理。在 Unix 上會被忽略。當指定了 shell 且為 CMD 時,這會自動設定為 true預設值: false
    • windowsHide <boolean> 隱藏通常會在 Windows 系統上建立的子程序主控台視窗。 預設值: false
    • signal <AbortSignal> 允許使用 AbortSignal 中止子程序。
    • timeout <number> 程序允許執行的最大時間(以毫秒為單位)。 預設值: undefined
    • killSignal <string> | <integer> 當衍生程序因逾時或中止訊號而被終止時使用的訊號值。 預設值: 'SIGTERM'
  • 回傳:<ChildProcess>

child_process.spawn() 方法使用給定的 command 衍生新程序,並在 args 中包含命令列引數。如果省略,args 預設為空陣列。

如果啟用了 shell 選項,請勿將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

第三個引數可用於指定額外的選項,這些是預設值:

const defaults = {
  cwd: undefined,
  env: process.env,
};

使用 cwd 指定衍生程序的工作目錄。如果未提供,預設是繼承目前的工作目錄。如果提供了但路徑不存在,子程序會發出 ENOENT 錯誤並立即結束。當指令不存在時,也會發出 ENOENT

使用 env 指定新程序可見的環境變數,預設為 process.env

env 中的 undefined 值將被忽略。

執行 ls -lh /usr,擷取 stdoutstderr 與結束代碼的範例:

const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});
import { spawn } from 'node:child_process';
import { once } from 'node:events';
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

const [code] = await once(ls, 'close');
console.log(`child process exited with code ${code}`);

範例:執行 ps ax | grep ssh 的一個非常精細的方法

const { spawn } = require('node:child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);

ps.stdout.on('data', (data) => {
  grep.stdin.write(data);
});

ps.stderr.on('data', (data) => {
  console.error(`ps stderr: ${data}`);
});

ps.on('close', (code) => {
  if (code !== 0) {
    console.log(`ps process exited with code ${code}`);
  }
  grep.stdin.end();
});

grep.stdout.on('data', (data) => {
  console.log(data.toString());
});

grep.stderr.on('data', (data) => {
  console.error(`grep stderr: ${data}`);
});

grep.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`);
  }
});
import { spawn } from 'node:child_process';
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);

ps.stdout.on('data', (data) => {
  grep.stdin.write(data);
});

ps.stderr.on('data', (data) => {
  console.error(`ps stderr: ${data}`);
});

ps.on('close', (code) => {
  if (code !== 0) {
    console.log(`ps process exited with code ${code}`);
  }
  grep.stdin.end();
});

grep.stdout.on('data', (data) => {
  console.log(data.toString());
});

grep.stderr.on('data', (data) => {
  console.error(`grep stderr: ${data}`);
});

grep.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`);
  }
});

檢查失敗的 spawn 範例:

const { spawn } = require('node:child_process');
const subprocess = spawn('bad_command');

subprocess.on('error', (err) => {
  console.error('Failed to start subprocess.');
});
import { spawn } from 'node:child_process';
const subprocess = spawn('bad_command');

subprocess.on('error', (err) => {
  console.error('Failed to start subprocess.');
});

某些平台(macOS、Linux)會使用 argv[0] 的值作為程序標題,而其他平台(Windows、SunOS)則會使用 command

Node.js 在啟動時會用 process.execPath 覆寫 argv[0],因此 Node.js 子程序中的 process.argv[0] 不會與父程序傳遞給 spawnargv0 參數相符。請改用 process.argv0 屬性來擷取它。

如果啟用了 signal 選項,在相應的 AbortController 上呼叫 .abort() 類似於在子程序上呼叫 .kill(),不同之處在於傳遞給回呼的錯誤將是 AbortError

const { spawn } = require('node:child_process');
const controller = new AbortController();
const { signal } = controller;
const grep = spawn('grep', ['ssh'], { signal });
grep.on('error', (err) => {
  // This will be called with err being an AbortError if the controller aborts
});
controller.abort(); // Stops the child process
import { spawn } from 'node:child_process';
const controller = new AbortController();
const { signal } = controller;
const grep = spawn('grep', ['ssh'], { signal });
grep.on('error', (err) => {
  // This will be called with err being an AbortError if the controller aborts
});
controller.abort(); // Stops the child process
options.detached#

在 Windows 上,將 options.detached 設定為 true 可以使子程序在父程序結束後繼續執行。子程序將擁有自己的主控台視窗。一旦為子程序啟用此功能,就無法停用。

在非 Windows 平台上,如果將 options.detached 設定為 true,子程序將成為新程序群組與工作階段的領導者。子程序在父程序結束後可能會繼續執行,無論它們是否分離。請參閱 setsid(2) 以取得更多資訊。

預設情況下,父程序會等待分離的子程序結束。若要防止父程序等待指定的 subprocess 結束,請使用 subprocess.unref() 方法。這樣做會使父程序的事件迴圈不將子程序包含在其參考計數中,允許父程序獨立於子程序結束,除非子程序與父程序之間建立了 IPC 頻道。

使用 detached 選項啟動長時間執行的程序時,除非為其提供了未連接到父程序的 stdio 配置,否則該程序在父程序結束後不會在背景持續執行。如果繼承了父程序的 stdio,則子程序將保持連接到控制終端機。

藉由分離並忽略其父程序的 stdio 檔案描述子,以忽略父程序終止的長時間執行程序範例:

const { spawn } = require('node:child_process');
const process = require('node:process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();
import { spawn } from 'node:child_process';
import process from 'node:process';

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();

或者,也可以將子程序的輸出重新導向到檔案中:

const { openSync } = require('node:fs');
const { spawn } = require('node:child_process');
const out = openSync('./out.log', 'a');
const err = openSync('./out.log', 'a');

const subprocess = spawn('prg', [], {
  detached: true,
  stdio: [ 'ignore', out, err ],
});

subprocess.unref();
import { openSync } from 'node:fs';
import { spawn } from 'node:child_process';
const out = openSync('./out.log', 'a');
const err = openSync('./out.log', 'a');

const subprocess = spawn('prg', [], {
  detached: true,
  stdio: [ 'ignore', out, err ],
});

subprocess.unref();
options.stdio#

options.stdio 選項用於配置父子程序之間建立的管道。預設情況下,子程序的 stdin、stdout 與 stderr 會重新導向到 ChildProcess 物件上對應的 subprocess.stdinsubprocess.stdoutsubprocess.stderr 串流。這相當於將 options.stdio 設定為 ['pipe', 'pipe', 'pipe']

為了方便起見,options.stdio 可以是以下字串之一:

  • 'pipe':相當於 ['pipe', 'pipe', 'pipe'](預設值)
  • 'overlapped':相當於 ['overlapped', 'overlapped', 'overlapped']
  • 'ignore':相當於 ['ignore', 'ignore', 'ignore']
  • 'inherit':相當於 ['inherit', 'inherit', 'inherit'][0, 1, 2]

否則,options.stdio 的值是一個陣列,其中每個索引對應子程序中的一個 fd。fd 0、1 與 2 分別對應 stdin、stdout 與 stderr。可以指定額外的 fd 以在父子程序之間建立額外的管道。值為以下之一:

  1. 'pipe':在子程序與父程序之間建立管道。管道的父端作為 child_process 物件上的屬性 subprocess.stdio[fd] 暴露給父端。為 fd 0、1 與 2 建立的管道也分別作為 subprocess.stdinsubprocess.stdoutsubprocess.stderr 使用。這些不是實際的 Unix 管道,因此子程序無法透過其描述子檔案(例如 /dev/fd/2/dev/stdout)來使用它們。

  2. 'overlapped':與 'pipe' 相同,不同之處在於控制代碼(handle)上設定了 FILE_FLAG_OVERLAPPED 標記。這對於子程序 stdio 控制代碼上的重疊 I/O(overlapped I/O)是必要的。請參閱 說明文件 以取得更多詳細資訊。在非 Windows 系統上,這與 'pipe' 完全相同。

  3. 'ipc':建立一個 IPC 頻道,用於在父子程序之間傳遞訊息/檔案描述子。一個 ChildProcess 最多只能有一個 IPC stdio 檔案描述子。設定此選項會啟用 subprocess.send() 方法。如果子程序是 Node.js 執行個體,則 IPC 頻道的存在將啟用子程序中的 process.send()process.disconnect() 方法,以及 'disconnect''message' 事件。

    不支援以 process.send() 以外的任何方式存取 IPC 頻道 fd,或將 IPC 頻道用於非 Node.js 執行個體的子程序。

  4. 'ignore':指示 Node.js 忽略子程序中的 fd。雖然 Node.js 始終會為其衍生的程序開啟 fd 0、1 與 2,但將 fd 設定為 'ignore' 會導致 Node.js 開啟 /dev/null 並將其附加到子程序的 fd。

  5. 'inherit':將對應的 stdio 串流與父程序互相導通。在前三個位置中,這分別相當於 process.stdinprocess.stdoutprocess.stderr。在任何其他位置,相當於 'ignore'

  6. <Stream> 物件:與子程序共享參照 TTY、檔案、通訊端(socket)或管道的可讀或可寫串流。串流底層的檔案描述子在子程序中會被複製到與 stdio 陣列索引對應的 fd。串流必須具有底層描述子(檔案串流直到 'open' 事件發生後才啟動)。注意:雖然技術上可以將 stdin 作為可寫串流傳遞,或將 stdout/stderr 作為可讀串流傳遞,但不建議這樣做。可讀與可寫串流的設計行為不同,若使用不當(例如在預期可寫串流的地方傳遞可讀串流)可能會導致非預期的結果或錯誤。這種做法是不被鼓勵的,因為如果串流遇到錯誤,可能會導致未定義的行為或遺失回呼。請務必確保 stdin 作為可讀串流使用,而 stdout/stderr 作為可寫串流使用,以維持父子程序之間預期的資料流。

  7. 正整數:整數值被解讀為在父程序中開啟的檔案描述子。它與子程序共享,類似於共享 <Stream> 物件的方式。在 Windows 上不支援傳遞通訊端。

  8. nullundefined:使用預設值。對於 stdio fd 0、1 與 2(換句話說,stdin、stdout 與 stderr),會建立管道。對於 fd 3 及以上,預設值為 'ignore'

const { spawn } = require('node:child_process');
const process = require('node:process');

// Child will use parent's stdios.
spawn('prg', [], { stdio: 'inherit' });

// Spawn child sharing only stderr.
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// Open an extra fd=4, to interact with programs presenting a
// startd-style interface.
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
import { spawn } from 'node:child_process';
import process from 'node:process';

// Child will use parent's stdios.
spawn('prg', [], { stdio: 'inherit' });

// Spawn child sharing only stderr.
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// Open an extra fd=4, to interact with programs presenting a
// startd-style interface.
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });

值得注意的是,當父子程序之間建立 IPC 頻道,且子程序是 Node.js 執行個體時,子程序在啟動時其 IPC 頻道是未經參考的(使用 unref()),直到子程序為 'disconnect' 事件或 'message' 事件註冊事件處理常式為止。這允許子程序正常結束,而不會因為開啟的 IPC 頻道而保持開啟。 另請參閱:child_process.exec()child_process.fork()

同步程序建立#

child_process.spawnSync()child_process.execSync()child_process.execFileSync() 方法是同步的,會阻塞 Node.js 事件迴圈,暫停執行任何額外的程式碼直到衍生的程序結束為止。

這類阻塞呼叫主要用於簡化一般用途的指令稿任務,以及簡化啟動時應用程式配置的載入/處理。

child_process.execFileSync(file[, args][, options])#

  • file <string> 要執行的執行檔名稱或路徑。
  • args <string[]> 字串引數列表。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 將作為 stdin 傳遞給衍生程序的值。如果 stdio[0] 設定為 'pipe',提供此值將覆蓋 stdio[0]
    • stdio <string> | <Array> 子程序的 stdio 配置。請參閱 child_process.spawn()stdio。除非指定了 stdio,否則 stderr 預設會輸出到父程序的 stderr。預設值: 'pipe'
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • uid <number> 設定程序的用戶識別碼(請參閱 setuid(2))。
    • gid <number> 設定程序的群組識別碼(請參閱 setgid(2))。
    • timeout <number> 程序允許執行的最大時間(以毫秒為單位)。 預設值: undefined
    • killSignal <string> | <integer> 當衍生程序被終止時要使用的訊號值。 預設值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 上允許的最大資料量(以位元組為單位)。如果超過,子程序將被終止。請參閱 maxBuffer 與 Unicode 中的警語。預設值: 1024 * 1024
    • encoding <string> 用於所有 stdio 輸入與輸出的編碼。 預設值: 'buffer'
    • windowsHide <boolean> 隱藏通常會在 Windows 系統上建立的子程序主控台視窗。 預設值: false
    • shell <boolean> | <string> 如果為 true,則在 shell 中執行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以將不同的 shell 指定為字串。請參閱 Shell 需求預設 Windows shell預設值: false(不使用 shell)。
  • 回傳:<Buffer> | <string> 指令的 stdout。

child_process.execFileSync() 方法通常與 child_process.execFile() 相同,不同之處在於該方法在子程序完全關閉之前不會回傳。當遇到逾時並發送 killSignal 時,該方法直到程序完全結束才會回傳。

如果子程序攔截並處理了 SIGTERM 訊號且未結束,父程序仍會等待直到子程序結束為止。

如果程序逾時或結束代碼不為零,此方法將擲回一個 Error,其中包含底層 child_process.spawnSync() 的完整結果。

如果啟用了 shell 選項,請勿將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

const { execFileSync } = require('node:child_process');

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // Capture stdout and stderr from child process. Overrides the
    // default behavior of streaming child stderr to the parent stderr
    stdio: 'pipe',

    // Use utf8 encoding for stdio pipes
    encoding: 'utf8',
  });

  console.log(stdout);
} catch (err) {
  if (err.code) {
    // Spawning child process failed
    console.error(err.code);
  } else {
    // Child was spawned but exited with non-zero exit code
    // Error contains any stdout and stderr from the child
    const { stdout, stderr } = err;

    console.error({ stdout, stderr });
  }
}
import { execFileSync } from 'node:child_process';

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    // Capture stdout and stderr from child process. Overrides the
    // default behavior of streaming child stderr to the parent stderr
    stdio: 'pipe',

    // Use utf8 encoding for stdio pipes
    encoding: 'utf8',
  });

  console.log(stdout);
} catch (err) {
  if (err.code) {
    // Spawning child process failed
    console.error(err.code);
  } else {
    // Child was spawned but exited with non-zero exit code
    // Error contains any stdout and stderr from the child
    const { stdout, stderr } = err;

    console.error({ stdout, stderr });
  }
}

child_process.execSync(command[, options])#

  • command <string> 要執行的指令。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 將作為 stdin 傳遞給衍生程序的值。如果 stdio[0] 設定為 'pipe',提供此值將覆蓋 stdio[0]
    • stdio <string> | <Array> 子程序的 stdio 配置。請參閱 child_process.spawn()stdio。除非指定了 stdio,否則 stderr 預設會輸出到父程序的 stderr。預設值: 'pipe'
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • shell <string> 用於執行指令的 shell。請參閱 Shell 需求預設 Windows shell預設值: Unix 上為 '/bin/sh',Windows 上為 process.env.ComSpec
    • uid <number> 設定程序的用戶識別碼。(請參閱 setuid(2))。
    • gid <number> 設定程序的群組識別碼。(請參閱 setgid(2))。
    • timeout <number> 程序允許執行的最大時間(以毫秒為單位)。 預設值: undefined
    • killSignal <string> | <integer> 當衍生程序被終止時要使用的訊號值。 預設值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 允許的最大資料量(以位元組為單位)。如果超過,子程序將被終止,且任何輸出都會被截斷。請參閱 maxBuffer 與 Unicode 中的警語。預設值: 1024 * 1024
    • encoding <string> 用於所有 stdio 輸入與輸出的編碼。 預設值: 'buffer'
    • windowsHide <boolean> 隱藏通常會在 Windows 系統上建立的子程序主控台視窗。 預設值: false
  • 回傳:<Buffer> | <string> 指令的 stdout。

child_process.execSync() 方法通常與 child_process.exec() 相同,不同之處在於該方法在子程序完全關閉之前不會回傳。當遇到逾時並發送 killSignal 時,該方法直到程序完全結束才會回傳。如果子程序攔截並處理了 SIGTERM 訊號且未結束,父程序將等待直到子程序結束為止。

如果程序逾時或結束代碼不為零,此方法將擲回。 Error 物件將包含來自 child_process.spawnSync() 的完整結果。

絕對不要將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

child_process.spawnSync(command[, args][, options])#

  • command <string> 要執行的指令。
  • args <string[]> 字串引數列表。
  • options <Object>
    • cwd <string> | <URL> 子程序的目前工作目錄。
    • input <string> | <Buffer> | <TypedArray> | <DataView> 將作為 stdin 傳遞給衍生程序的值。如果 stdio[0] 設定為 'pipe',提供此值將覆蓋 stdio[0]
    • argv0 <string> 明確設定傳送至子程序的 argv[0] 值。如果未指定,則會被設定為 command
    • stdio <string> | <Array> 子程序的 stdio 配置。請參閱 child_process.spawn()stdio預設值: 'pipe'
    • env <Object> 環境變數鍵值對。 預設值: process.env
    • uid <number> 設定程序的用戶識別碼(請參閱 setuid(2))。
    • gid <number> 設定程序的群組識別碼(請參閱 setgid(2))。
    • timeout <number> 程序允許執行的最大時間(以毫秒為單位)。 預設值: undefined
    • killSignal <string> | <integer> 當衍生程序被終止時要使用的訊號值。 預設值: 'SIGTERM'
    • maxBuffer <number> stdout 或 stderr 允許的最大資料量(以位元組為單位)。如果超過,子程序將被終止,且任何輸出都會被截斷。請參閱 maxBuffer 與 Unicode 中的警語。預設值: 1024 * 1024
    • encoding <string> 用於所有 stdio 輸入與輸出的編碼。 預設值: 'buffer'
    • shell <boolean> | <string> 如果為 true,則在 shell 中執行 command。在 Unix 上使用 '/bin/sh',在 Windows 上使用 process.env.ComSpec。可以將不同的 shell 指定為字串。請參閱 Shell 需求預設 Windows shell預設值: false(不使用 shell)。
    • windowsVerbatimArguments <boolean> 在 Windows 上不對引數進行引號或逸出處理。在 Unix 上會被忽略。當指定了 shell 且為 CMD 時,這會自動設定為 true預設值: false
    • windowsHide <boolean> 隱藏通常會在 Windows 系統上建立的子程序主控台視窗。 預設值: false
  • 傳回:<Object>
    • pid <number> 子程序的 Pid。
    • output <Array> 來自 stdio 輸出的結果陣列。
    • stdout <Buffer> | <string> output[1] 的內容。
    • stderr <Buffer> | <string> output[2] 的內容。
    • status <number> | <null> 子程序的結束代碼,如果子程序因訊號終止則為 null
    • signal <string> | <null> 用於殺死子程序的訊號,如果子程序未因訊號終止則為 null
    • error <Error> 如果子程序失敗或逾時,則為錯誤物件。

child_process.spawnSync() 方法通常與 child_process.spawn() 相同,不同之處在於該函式直到子程序完全關閉才會回傳。當遇到逾時並發送 killSignal 時,該方法直到程序完全結束才會回傳。如果程序攔截並處理了 SIGTERM 訊號且未結束,父程序將等待直到子程序結束為止。

如果啟用了 shell 選項,請勿將未經清理的使用者輸入傳遞給此函式。任何包含 shell 元字元的輸入都可能被用來觸發任意指令執行。

類別:ChildProcess#

ChildProcess 的執行個體代表衍生的子程序。

ChildProcess 的執行個體不打算直接建立。相反地,請使用 child_process.spawn()child_process.exec()child_process.execFile()child_process.fork() 方法來建立 ChildProcess 的執行個體。

事件:'close'#

  • code <number> 如果子程序自行結束,則為結束代碼;如果子程序因訊號終止,則為 null
  • signal <string> 子程序終止所使用的訊號;如果子程序未因訊號終止,則為 null

'close' 事件在程序結束 子程序的 stdio 串流已關閉後發出。這與 'exit' 事件不同,因為多個程序可能共享相同的 stdio 串流。'close' 事件始終會在 'exit' 已經發出之後發出,或者在子程序衍生失敗時於 'error' 之後發出。

如果程序已結束,code 是程序的最終結束代碼,否則為 null。如果程序因收到訊號而終止,signal 是訊號的字串名稱,否則為 null。兩者之一始終不會是 null

const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process close all stdio with code ${code}`);
});

ls.on('exit', (code) => {
  console.log(`child process exited with code ${code}`);
});
import { spawn } from 'node:child_process';
import { once } from 'node:events';
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process close all stdio with code ${code}`);
});

ls.on('exit', (code) => {
  console.log(`child process exited with code ${code}`);
});

const [code] = await once(ls, 'close');
console.log(`child process close all stdio with code ${code}`);

事件:'disconnect'#

'disconnect' 事件是在父程序中呼叫 subprocess.disconnect() 方法,或在子程序中呼叫 process.disconnect() 後發出的。中斷連接後,就無法再傳送或接收訊息,且 subprocess.connected 屬性為 false

事件:'error'#

'error' 事件在發生以下情況時發出:

  • 無法衍生程序。
  • 無法殺死程序。
  • 向子程序傳送訊息失敗。
  • 子程序透過 signal 選項中止。

發生錯誤後,'exit' 事件可能會也可能不會觸發。同時接聽 'exit''error' 事件時,請防止意外多次呼叫處理常式函式。

另請參閱 subprocess.kill()subprocess.send()

事件:'exit'#

  • code <number> 如果子程序自行結束,則為結束代碼;如果子程序因訊號終止,則為 null
  • signal <string> 子程序終止所使用的訊號;如果子程序未因訊號終止,則為 null

'exit' 事件在子程序結束後發出。如果程序已結束,code 是程序的最終結束代碼,否則為 null。如果程序因收到訊號而終止,signal 是訊號的字串名稱,否則為 null。兩者之一始終不會是 null

'exit' 事件觸發時,子程序 stdio 串流可能仍處於開啟狀態。

Node.js 為 SIGINTSIGTERM 建立了訊號處理常式,Node.js 程序不會因為收到這些訊號而立即終止。相反地,Node.js 會執行一系列清理動作,然後重新發出處理過的訊號。

請參閱 waitpid(2)

code 因訊號終止而為 null 時,你可以使用 util.convertProcessSignalToExitCode() 將訊號轉換為 POSIX 結束代碼。

事件:'message'#

當子程序使用 process.send() 傳送訊息時,會觸發 'message' 事件。

訊息會經過序列化與解析。產生的訊息可能與原始傳送的訊息不同。

如果在衍生子程序時將 serialization 選項設定為 'advanced',則 message 引數可以包含 JSON 無法表示的資料。請參閱 進階序列化 以取得更多詳細資訊。

事件:'spawn'#

'spawn' 事件在子程序衍生成功後發出。如果子程序衍生不成功,則不會發出 'spawn' 事件,而是發出 'error' 事件。

如果發出 'spawn' 事件,它會出現在所有其他事件之前,且在透過 stdoutstderr 接收任何資料之前。

無論衍生程序 內部 是否發生錯誤,'spawn' 事件都會觸發。例如,如果 bash some-command 成功衍生,則 'spawn' 事件將觸發,儘管 bash 可能無法衍生 some-command。在使用 { shell: true } 時,此警語也適用。

subprocess.channel#

  • 類型:<Object> 代表指向子程序的 IPC 頻道的管道。

subprocess.channel 屬性是對子程序 IPC 頻道的參考。如果不存在 IPC 頻道,則此屬性為 undefined

subprocess.channel.ref()#

如果先前已呼叫過 .unref(),此方法會讓 IPC 頻道保持父程序的事件迴圈執行。

subprocess.channel.unref()#

此方法讓 IPC 頻道不保持父程序的事件迴圈執行,並讓其即使在頻道開啟時也能結束。

subprocess.connected#

  • 類型:<boolean> 在呼叫 subprocess.disconnect() 後設定為 false

subprocess.connected 屬性表示是否仍可從子程序傳送與接收訊息。當 subprocess.connectedfalse 時,就無法再傳送或接收訊息。

subprocess.disconnect()#

關閉父子程序之間的 IPC 頻道,一旦沒有其他連接保持其存活,子程序就可以正常退出。呼叫此方法後,父子程序中的 subprocess.connectedprocess.connected 屬性(分別)都將設定為 false,且無法再於程序之間傳遞訊息。

當沒有正在接收的訊息時,將發出 'disconnect' 事件。這通常會在呼叫 subprocess.disconnect() 後立即觸發。

當子程序是 Node.js 執行個體(例如使用 child_process.fork() 衍生)時,也可以在子程序中呼叫 process.disconnect() 方法來關閉 IPC 頻道。

subprocess.exitCode#

subprocess.exitCode 屬性表示子程序的結束代碼。如果子程序仍在執行中,該欄位將為 null

當子程序因訊號終止時,subprocess.exitCode 將為 null 且會設定 subprocess.signalCode。若要取得對應的 POSIX 結束代碼,請使用 util.convertProcessSignalToExitCode(subprocess.signalCode)

subprocess.kill([signal])#

subprocess.kill() 方法向子程序傳送訊號。如果未提供引數,程序將被傳送 'SIGTERM' 訊號。請參閱 signal(7) 以取得可用訊號列表。如果 kill(2) 成功,此函式回傳 true,否則回傳 false

const { spawn } = require('node:child_process');
const grep = spawn('grep', ['ssh']);

grep.on('close', (code, signal) => {
  console.log(
    `child process terminated due to receipt of signal ${signal}`);
});

// Send SIGHUP to process.
grep.kill('SIGHUP');
import { spawn } from 'node:child_process';
const grep = spawn('grep', ['ssh']);

grep.on('close', (code, signal) => {
  console.log(
    `child process terminated due to receipt of signal ${signal}`);
});

// Send SIGHUP to process.
grep.kill('SIGHUP');

如果無法傳送訊號,ChildProcess 物件可能會發出 'error' 事件。向已經結束的子程序傳送訊號不是錯誤,但可能會產生不可預見的後果。特別是,如果程序識別碼 (PID) 已重新指派給另一個程序,則訊號將傳送到該程序,這可能會產生非預期的結果。

雖然該函式名為 kill,但傳遞給子程序的訊號實際上可能不會終止該程序。

請參閱 kill(2) 作為參考。

在沒有 POSIX 訊號的 Windows 上,除了 'SIGKILL''SIGTERM''SIGINT''SIGQUIT' 之外,signal 引數將被忽略,且程序將始終被強制且突然地殺死(類似於 'SIGKILL')。請參閱 訊號事件 以取得更多詳細資訊。

在 Linux 上,當嘗試殺死父程序時,子程序的子程序不會被終止。這在 shell 中執行新程序或使用 ChildProcessshell 選項時很可能會發生。

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

const subprocess = spawn(
  'sh',
  [
    '-c',
    `node -e "setInterval(() => {
      console.log(process.pid, 'is alive')
    }, 500);"`,
  ], {
    stdio: ['inherit', 'inherit', 'inherit'],
  },
);

setTimeout(() => {
  subprocess.kill(); // Does not terminate the Node.js process in the shell.
}, 2000);
import { spawn } from 'node:child_process';

const subprocess = spawn(
  'sh',
  [
    '-c',
    `node -e "setInterval(() => {
      console.log(process.pid, 'is alive')
    }, 500);"`,
  ], {
    stdio: ['inherit', 'inherit', 'inherit'],
  },
);

setTimeout(() => {
  subprocess.kill(); // Does not terminate the Node.js process in the shell.
}, 2000);

subprocess[Symbol.dispose]()#

使用 'SIGTERM' 呼叫 subprocess.kill()

subprocess.killed#

  • 類型:<boolean> 在使用 subprocess.kill() 成功向子程序傳送訊號後設定為 true

subprocess.killed 屬性表示子程序是否成功收到來自 subprocess.kill() 的訊號。killed 屬性不表示子程序已經終止。

subprocess.pid#

回傳子程序的程序識別碼 (PID)。如果子程序因錯誤而衍生失敗,則值為 undefined 並會發出 error

const { spawn } = require('node:child_process');
const grep = spawn('grep', ['ssh']);

console.log(`Spawned child pid: ${grep.pid}`);
grep.stdin.end();
import { spawn } from 'node:child_process';
const grep = spawn('grep', ['ssh']);

console.log(`Spawned child pid: ${grep.pid}`);
grep.stdin.end();

subprocess.ref()#

在呼叫 subprocess.unref() 後呼叫 subprocess.ref() 將還原已移除的子程序參考計數,強制父程序在結束前等待子程序結束。

const { spawn } = require('node:child_process');
const process = require('node:process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();
subprocess.ref();
import { spawn } from 'node:child_process';
import process from 'node:process';

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();
subprocess.ref();

subprocess.send(message[, sendHandle[, options]][, callback])#

當父子程序之間建立了 IPC 頻道時(即使用 child_process.fork() 時),可以使用 subprocess.send() 方法向子程序傳送訊息。當子程序是 Node.js 執行個體時,可以透過 'message' 事件接收這些訊息。

訊息會經過序列化與解析。產生的訊息可能與原始傳送的訊息不同。

例如,在父指令稿中:

const { fork } = require('node:child_process');
const forkedProcess = fork(`${__dirname}/sub.js`);

forkedProcess.on('message', (message) => {
  console.log('PARENT got message:', message);
});

// Causes the child to print: CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' });
import { fork } from 'node:child_process';
const forkedProcess = fork(`${import.meta.dirname}/sub.js`);

forkedProcess.on('message', (message) => {
  console.log('PARENT got message:', message);
});

// Causes the child to print: CHILD got message: { hello: 'world' }
forkedProcess.send({ hello: 'world' });

然後子指令稿 'sub.js' 可能長這樣:

process.on('message', (message) => {
  console.log('CHILD got message:', message);
});

// Causes the parent to print: PARENT got message: { foo: 'bar', baz: null }
process.send({ foo: 'bar', baz: NaN });

Node.js 子程序將擁有自己的 process.send() 方法,允許子程序將訊息傳回給父程序。

傳送 {cmd: 'NODE_foo'} 訊息時有一個特殊情況。在 cmd 屬性中包含 NODE_ 前綴的訊息保留給 Node.js 核心內部使用,且不會在子程序的 'message' 事件中發出。相反地,此類訊息是使用 'internalMessage' 事件發出的,並由 Node.js 內部取用。應用程式應避免使用此類訊息或接聽 'internalMessage' 事件,因為它可能會在不另行通知的情況下發生變化。

可以傳遞給 subprocess.send() 的選用 sendHandle 引數用於將 TCP 伺服器或通訊端物件傳遞給子程序。子程序將接收該物件作為傳遞給 'message' 事件註冊的回呼函式的第二個引數。在通訊端中接收並緩衝的任何資料都不會傳送給子程序。在 Windows 上不支援傳送 IPC 通訊端。

選用的 callback 是一個在訊息傳送後、子程序可能尚未收到之前呼叫的函式。該函式在呼叫時帶有一個引數:成功時為 null,失敗時為 Error 物件。

如果未提供 callback 函式且訊息無法傳送,則 ChildProcess 物件將發出 'error' 事件。例如,當子程序已經結束時,就可能會發生這種情況。

如果頻道已關閉,或者未傳送訊息的積壓超過了使其不宜傳送更多的閾值,subprocess.send() 將回傳 false。否則,該方法回傳 truecallback 函式可用於實作流量控制。

範例:傳送伺服器物件#

sendHandle 引數可用於(例如)將 TCP 伺服器物件的控制代碼傳遞給子程序,如下例所示:

const { fork } = require('node:child_process');
const { createServer } = require('node:net');

const subprocess = fork('subprocess.js');

// Open up the server object and send the handle.
const server = createServer();
server.on('connection', (socket) => {
  socket.end('handled by parent');
});
server.listen(1337, () => {
  subprocess.send('server', server);
});
import { fork } from 'node:child_process';
import { createServer } from 'node:net';

const subprocess = fork('subprocess.js');

// Open up the server object and send the handle.
const server = createServer();
server.on('connection', (socket) => {
  socket.end('handled by parent');
});
server.listen(1337, () => {
  subprocess.send('server', server);
});

子程序隨後將伺服器物件接收為:

process.on('message', (m, server) => {
  if (m === 'server') {
    server.on('connection', (socket) => {
      socket.end('handled by child');
    });
  }
});

一旦伺服器現在由父子程序共享,某些連接可以由父程序處理,某些由子程序處理。

雖然上面的範例使用 node:net 模組建立的伺服器,但 node:dgram 模組伺服器使用完全相同的工作流程,例外情況是接聽 'message' 事件而不是 'connection',以及使用 server.bind() 而不是 server.listen()。然而,這僅在 Unix 平台上受支援。

範例:傳送通訊端物件#

類似地,sendHandler 引數可用於將通訊端的控制代碼傳遞給子程序。下面的範例衍生了兩個子程序,分別處理具有「一般」或「特殊」優先權的連接:

const { fork } = require('node:child_process');
const { createServer } = require('node:net');

const normal = fork('subprocess.js', ['normal']);
const special = fork('subprocess.js', ['special']);

// Open up the server and send sockets to child. Use pauseOnConnect to prevent
// the sockets from being read before they are sent to the child process.
const server = createServer({ pauseOnConnect: true });
server.on('connection', (socket) => {

  // If this is special priority...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
    return;
  }
  // This is normal priority.
  normal.send('socket', socket);
});
server.listen(1337);
import { fork } from 'node:child_process';
import { createServer } from 'node:net';

const normal = fork('subprocess.js', ['normal']);
const special = fork('subprocess.js', ['special']);

// Open up the server and send sockets to child. Use pauseOnConnect to prevent
// the sockets from being read before they are sent to the child process.
const server = createServer({ pauseOnConnect: true });
server.on('connection', (socket) => {

  // If this is special priority...
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
    return;
  }
  // This is normal priority.
  normal.send('socket', socket);
});
server.listen(1337);

subprocess.js 將接收通訊端控制代碼作為傳遞給事件回呼函式的第二個引數:

process.on('message', (m, socket) => {
  if (m === 'socket') {
    if (socket) {
      // Check that the client socket exists.
      // It is possible for the socket to be closed between the time it is
      // sent and the time it is received in the child process.
      socket.end(`Request handled with ${process.argv[2]} priority`);
    }
  }
});

不要在已傳遞給子程序的通訊端上使用 .maxConnections。父程序無法追蹤通訊端何時被銷毀。

子程序中的任何 'message' 處理常式都應驗證 socket 是否存在,因為在將連接傳送給子程序所需的期間,連接可能已經關閉。

subprocess.signalCode#

subprocess.signalCode 屬性表示子程序收到的訊號(如果有),否則為 null

當子程序因訊號終止時,subprocess.exitCode 將為 null。若要取得對應的 POSIX 結束代碼,請使用 util.convertProcessSignalToExitCode(subprocess.signalCode)

subprocess.spawnargs#

subprocess.spawnargs 屬性代表子程序啟動時所使用的完整命令列引數列表。

subprocess.spawnfile#

subprocess.spawnfile 屬性表示啟動的子程序執行檔名稱。

對於 child_process.fork(),其值將等於 process.execPath。對於 child_process.spawn(),其值將是執行檔的名稱。對於 child_process.exec(),其值將是啟動子程序的 shell 名稱。

subprocess.stderr#

代表子程序 stderrReadable Stream

如果子程序在衍生時 stdio[2] 設定為 'pipe' 以外的任何值,則此值將為 null

subprocess.stderrsubprocess.stdio[2] 的別名。兩個屬性都將引用相同的值。

如果子程序無法成功衍生,則 subprocess.stderr 屬性可能是 nullundefined

subprocess.stdin#

代表子程序 stdinWritable Stream

如果子程序等待讀取其所有輸入,則直到透過 end() 關閉此串流為止,子程序才會繼續執行。

如果子程序在衍生時 stdio[0] 設定為 'pipe' 以外的任何值,則此值將為 null

subprocess.stdinsubprocess.stdio[0] 的別名。這兩個屬性將指向相同的值。

如果子程序無法成功產生 (spawn),subprocess.stdin 屬性可能是 nullundefined

subprocess.stdio#

一個指向子程序的管線 (pipe) 稀疏陣列,對應於傳遞給 child_process.spawn()stdio 選項中被設定為 'pipe' 的位置。subprocess.stdio[0]subprocess.stdio[1]subprocess.stdio[2] 也可以分別透過 subprocess.stdinsubprocess.stdoutsubprocess.stderr 取得。

在以下範例中,僅子程序的 fd 1 (stdout) 被配置為管線,因此僅父程序的 subprocess.stdio[1] 是一個串流 (stream),陣列中的所有其他值皆為 null

const assert = require('node:assert');
const fs = require('node:fs');
const child_process = require('node:child_process');

const subprocess = child_process.spawn('ls', {
  stdio: [
    0, // Use parent's stdin for child.
    'pipe', // Pipe child's stdout to parent.
    fs.openSync('err.out', 'w'), // Direct child's stderr to a file.
  ],
});

assert.strictEqual(subprocess.stdio[0], null);
assert.strictEqual(subprocess.stdio[0], subprocess.stdin);

assert(subprocess.stdout);
assert.strictEqual(subprocess.stdio[1], subprocess.stdout);

assert.strictEqual(subprocess.stdio[2], null);
assert.strictEqual(subprocess.stdio[2], subprocess.stderr);
import assert from 'node:assert';
import fs from 'node:fs';
import child_process from 'node:child_process';

const subprocess = child_process.spawn('ls', {
  stdio: [
    0, // Use parent's stdin for child.
    'pipe', // Pipe child's stdout to parent.
    fs.openSync('err.out', 'w'), // Direct child's stderr to a file.
  ],
});

assert.strictEqual(subprocess.stdio[0], null);
assert.strictEqual(subprocess.stdio[0], subprocess.stdin);

assert(subprocess.stdout);
assert.strictEqual(subprocess.stdio[1], subprocess.stdout);

assert.strictEqual(subprocess.stdio[2], null);
assert.strictEqual(subprocess.stdio[2], subprocess.stderr);

如果子程序無法成功產生,subprocess.stdio 屬性可能是 undefined

subprocess.stdout#

一個代表子程序 stdoutReadable Stream(可讀取串流)。

如果子程序在產生時將 stdio[1] 設定為 'pipe' 以外的任何值,則此值將為 null

subprocess.stdoutsubprocess.stdio[1] 的別名。這兩個屬性將指向相同的值。

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

const subprocess = spawn('ls');

subprocess.stdout.on('data', (data) => {
  console.log(`Received chunk ${data}`);
});
import { spawn } from 'node:child_process';

const subprocess = spawn('ls');

subprocess.stdout.on('data', (data) => {
  console.log(`Received chunk ${data}`);
});

如果子程序無法成功產生,subprocess.stdout 屬性可能是 nullundefined

subprocess.unref()#

預設情況下,父程序會等待分離的 (detached) 子程序結束。若要防止父程序等待特定的 subprocess 結束,請使用 subprocess.unref() 方法。這樣做會使父程序的事件迴圈 (event loop) 在其參照計數中排除該子程序,讓父程序可以獨立於子程序結束,除非子程序與父程序之間建立了 IPC 頻道。

const { spawn } = require('node:child_process');
const process = require('node:process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();
import { spawn } from 'node:child_process';
import process from 'node:process';

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();

maxBuffer 與 Unicode#

maxBuffer 選項指定了 stdoutstderr 允許的最大位元組數。如果超過此值,子程序將被終止。這會影響包含多位元組字元編碼(如 UTF-8 或 UTF-16)的輸出。例如,console.log('中文测试') 雖然只有 4 個字元,但會向 stdout 發送 13 個 UTF-8 編碼的位元組。

Shell 要求#

Shell 應能理解 -c 開關。如果 Shell 是 'cmd.exe',它應能理解 /d /s /c 開關,且命令列解析應具備相容性。

預設 Windows Shell#

雖然 Microsoft 規定 %COMSPEC% 在根環境中必須包含 'cmd.exe' 的路徑,但子程序並不總是受限於同樣的要求。因此,在可以產生 Shell 的 child_process 函式中,如果 process.env.ComSpec 不可用,則會使用 'cmd.exe' 作為備用方案。

進階序列化#

子程序支援一種基於 node:v8 模組序列化 API 的 IPC 序列化機制,該機制基於 HTML 結構化複製演算法 (structured clone algorithm)。這通常功能更強大,並支援更多內建的 JavaScript 物件類型,例如 BigIntMapSetArrayBufferTypedArrayBufferErrorRegExp 等。

然而,此格式並非 JSON 的完全超集,例如在這些內建類型的物件上設定的屬性將不會在序列化步驟中傳遞。此外,根據所傳遞資料的結構,效能可能與 JSON 不等同。因此,此功能需要在使用 child_process.spawn()child_process.fork() 時,透過將 serialization 選項設定為 'advanced' 來啟用。