spawn()
spawn()
的運作方式spawnSync()
spawn()
的非同步輔助函數
exec()
execFile()
spawnAsync()
的同步輔助函數
execSync()
execFileSync()
'node:child_process'
的函式在本章節中,我們將探討如何透過模組 'node:child_process'
從 Node.js 執行 shell 指令。
模組 'node:child_process'
有兩個版本的函式用於執行 shell 指令(在已衍生的子程序中)
spawn()
。spawnSync()
。我們將先探討 spawn()
,再探討 spawnSync()
。最後,我們將檢視以下基於它們且相當類似的函式
spawn()
exec()
execFile()
spawnSync()
execSync()
execFileSync()
本章節中顯示的程式碼會在 Unix 上執行,但我也在 Windows 上測試過,大部分程式碼只需做些微更動(例如將結尾換行符號改為 '\r\n'
而不是 '\n'
)即可執行。
以下功能常出現在範例中。因此,在此一次說明
斷言:assert.equal()
用於基本值,assert.deepEqual()
用於物件。範例中從未顯示必要的匯入。
import * as assert from 'node:assert/strict';
函式 Readable.toWeb()
將 Node 的原生 stream.Readable
轉換為網路串流(ReadableStream
的實例)。在 §10「在 Node.js 上使用網路串流」 中說明。範例中總是會匯入 Readable
。
非同步函式 readableStreamToString()
會使用可讀取的網路串流,並傳回字串(包裝在 Promise 中)。在 網路串流章節 中說明。假設範例中可以使用此函式。
spawn()
spawn()
的運作方式spawn(
: string,
command?: Array<string>,
args?: Object
options: ChildProcess )
spawn()
會在新的程序中非同步執行指令:此程序會與 Node 的主要 JavaScript 程序並行執行,我們可以使用各種方式(通常透過串流)與它通訊。
接下來,說明 spawn()
的參數和結果。如果您偏好透過範例學習,可以跳過該內容,繼續閱讀以下小節。
command
command
是包含 shell 指令的字串。使用此參數有兩種模式
args
,而 command
包含整個 shell 命令。我們甚至可以使用 shell 功能,例如在多個可執行檔之間進行管道處理,將 I/O 重新導向到檔案、變數和萬用字元。
options.shell
必須為 true
,因為我們需要一個 shell 來處理 shell 功能。command
僅包含命令的名稱,而 args
則包含其參數。
options.shell
為 true
,則會對參數中的許多元字元進行詮釋,並且萬用字元和變數名稱等功能會起作用。options.shell
為 false
,則會逐字使用字串,而且我們永遠不必跳脫元字元。兩種模式都會在 本章稍後 進行示範。
options
以下 options
最有趣
.shell: boolean|string
(預設:false
)true
。例如,否則無法執行 .bat
和 .cmd
檔案。.shell
為 false
,則僅核心 shell 功能(例如管道處理、I/O 重新導向、檔案名稱萬用字元和變數)不可用。.shell
為 true
,我們必須小心使用者的輸入並對其進行消毒,因為很容易執行任意程式碼。如果我們想將元字元用作非元字元,我們還必須跳脫元字元。.shell
設定為 shell 可執行檔的路徑。然後,Node.js 會使用該可執行檔來執行命令。如果我們將 .shell
設定為 true
,Node.js 會使用
'/bin/sh'
process.env.ComSpec
.cwd: 字串 | URL
.stdio: Array<字串|串流>|字串
.env: 物件
(預設:process.env
)查看 process.env
(例如在 Node.js REPL 中)以查看有哪些變數存在。
我們可以使用擴散來非破壞性地覆寫現有變數,或者在它尚未存在時建立它
env: {...process.env, MY_VAR: 'Hi!'}} {
.signal: 中止訊號
ac
,我們可以將 ac.signal
傳遞給 spawn()
並透過 ac.abort()
中止子處理序。這在 本章稍後 會進行示範。.timeout: 數字
.timeout
毫秒,它就會被終止。options.stdio
子程序的每個標準 I/O 串流都有數字 ID,稱為檔案描述符
檔案描述符可以更多,但這很少見。
options.stdio
設定子程序的串流是否以及如何傳遞到父程序的串流。它可以是陣列,其中每個元素設定等於其索引的檔案描述符。下列值可用作陣列元素
'pipe'
:
childProcess.stdin
傳遞到子項目的 stdin。請注意,儘管其名稱,前者是屬於父程序的串流。childProcess.stdout
。childProcess.stderr
。'ignore'
:忽略子項目的串流。
'inherit'
:將子項目的串流傳遞到父程序的對應串流。
'inherit'
。原生 Node.js 串流:傳遞到或從該串流。
也支援其他值,但這超出了本章的範圍。
我們也可以縮寫,而不是透過陣列指定 options.stdio
'pipe'
等於 ['pipe', 'pipe', 'pipe']
(options.stdio
的預設值)。'ignore'
等於 ['ignore', 'ignore', 'ignore']
。'inherit'
等於 ['inherit', 'inherit', 'inherit']
。ChildProcess
執行個體spawn()
傳回 ChildProcess
的執行個體。
有趣的資料屬性
.exitCode:數字 | null
null
表示程序尚未退出。.signalCode:字串 | null
null
。有關更多資訊,請參閱下方方法 .kill()
的說明。.stdin
.stdout
.stderr
.pid:數字 | 未定義
.pid
會是 undefined
。這個值會在呼叫 spawn()
後立即提供。有趣的函式
.kill(signalCode?: number | string = 'SIGTERM'): boolean
傳送 POSIX 訊號給子程序 (通常會導致程序終止)
signal
的手冊頁面 包含一個值清單。SIGINT
、SIGTERM
和 SIGKILL
。有關更多資訊,請參閱 Node.js 文件。這個函式會在 本章節稍後 進行示範。
有趣的事件
.on('exit', (exitCode: number|null, signalCode: string|null) => {})
'close'
會在子程序結束後所有 stdio 串流關閉時通知我們。.on('error', (err: Error) => {})
'exit'
事件。我們稍後會看到 如何將事件轉換成可以等待的 Promise。
使用非同步 spawn()
時,指令的子程序會非同步啟動。下列程式碼示範了這一點
import {spawn} from 'node:child_process';
spawn(
'echo', ['Command starts'],
{stdio: 'inherit',
shell: true,
};
)console.log('After spawn()');
輸出結果為
After spawn()
Command starts
在本節中,我們使用兩種方式指定相同的指令呼叫
command
提供完整的呼叫。command
提供指令,並透過第二個參數 args
提供其參數。import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
const childProcess = spawn(
'echo "Hello, how are you?"',
{shell: true, // (A)
stdio: ['ignore', 'pipe', 'inherit'], // (B)
};
)const stdout = Readable.toWeb(
.stdout.setEncoding('utf-8'));
childProcess
// Result on Unix
.equal(
assertawait readableStreamToString(stdout),
'Hello, how are you?\n' // (C)
;
)
// Result on Windows: '"Hello, how are you?"\r\n'
每個帶有參數的僅指令產生都需要 .shell
為 true
(A 行),即使它像這個範例一樣簡單。
在 B 行,我們告訴 spawn()
如何處理標準 I/O
childProcess.stdout
(屬於父程序的串流)。在這種情況下,我們只對子程序的輸出感興趣。因此,一旦我們處理完輸出,我們就完成了。在其他情況下,我們可能必須等到子程序退出。稍後會說明如何執行此操作。
在僅命令模式下,我們看到更多 shell 的特殊性 - 例如,Windows 命令 shell 輸出包含雙引號(最後一行)。
import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
const childProcess = spawn(
'echo', ['Hello, how are you?'],
{shell: true,
stdio: ['ignore', 'pipe', 'inherit'],
};
)const stdout = Readable.toWeb(
.stdout.setEncoding('utf-8'));
childProcess
// Result on Unix
.equal(
assertawait readableStreamToString(stdout),
'Hello, how are you?\n'
;
)// Result on Windows: 'Hello, how are you?\r\n'
args
中的元字元讓我們探討一下如果 args
中有元字元會發生什麼情況
import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
async function echoUser({shell, args}) {
const childProcess = spawn(
`echo`, args,
{stdio: ['ignore', 'pipe', 'inherit'],
,
shell
};
)const stdout = Readable.toWeb(
.stdout.setEncoding('utf-8'));
childProcessreturn readableStreamToString(stdout);
}
// Results on Unix
.equal(
assertawait echoUser({shell: false, args: ['$USER']}), // (A)
'$USER\n'
;
).equal(
assertawait echoUser({shell: true, args: ['$USER']}), // (B)
'rauschma\n'
;
).equal(
assertawait echoUser({shell: true, args: [String.raw`\$USER`]}), // (C)
'$USER\n'
; )
$
) 等元字元不會產生任何效果(A 行)。$USER
會被解釋為變數(B 行)。其他元字元,例如星號 (*
),也會產生類似的效果。
這些是 Unix shell 元字元的兩個範例。Windows shell 有自己的元字元和跳脫方式。
讓我們使用更多 shell 功能(需要僅命令模式)
import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
import {EOL} from 'node:os';
const childProcess = spawn(
`(echo cherry && echo apple && echo banana) | sort`,
{stdio: ['ignore', 'pipe', 'inherit'],
shell: true,
};
)const stdout = Readable.toWeb(
.stdout.setEncoding('utf-8'));
childProcess.equal(
assertawait readableStreamToString(stdout),
'apple\nbanana\ncherry\n'
; )
到目前為止,我們只讀取子程序的標準輸出。但我們也可以將資料傳送至標準輸入
import {Readable, Writable} from 'node:stream';
import {spawn} from 'node:child_process';
const childProcess = spawn(
`sort`, // (A)
{stdio: ['pipe', 'pipe', 'inherit'],
};
)const stdin = Writable.toWeb(childProcess.stdin); // (B)
const writer = stdin.getWriter(); // (C)
try {
await writer.write('Cherry\n');
await writer.write('Apple\n');
await writer.write('Banana\n');
finally {
} .close();
writer
}
const stdout = Readable.toWeb(
.stdout.setEncoding('utf-8'));
childProcess.equal(
assertawait readableStreamToString(stdout),
'Apple\nBanana\nCherry\n'
; )
我們使用 shell 命令 sort
(A 行)為我們整理文字列。
在 B 行中,我們使用 Writable.toWeb()
將原生 Node.js 串流轉換為 Web 串流(有關更多資訊,請參閱 §10「在 Node.js 上使用 Web 串流」)。
如何透過撰寫器寫入 WritableStream(C 行)也說明在 Web 串流章節 中。
我們先前讓 shell 執行下列命令
(echo cherry && echo apple && echo banana) | sort
在以下範例中,我們手動執行管道傳輸,從回音(A 行)到排序(B 行)
import {Readable, Writable} from 'node:stream';
import {spawn} from 'node:child_process';
const echo = spawn( // (A)
`echo cherry && echo apple && echo banana`,
{stdio: ['ignore', 'pipe', 'inherit'],
shell: true,
};
)const sort = spawn( // (B)
`sort`,
{stdio: ['pipe', 'pipe', 'inherit'],
shell: true,
};
)
//==== Transferring chunks from echo.stdout to sort.stdin ====
const echoOut = Readable.toWeb(
.stdout.setEncoding('utf-8'));
echoconst sortIn = Writable.toWeb(sort.stdin);
const sortInWriter = sortIn.getWriter();
try {
for await (const chunk of echoOut) { // (C)
await sortInWriter.write(chunk);
}finally {
} .close();
sortInWriter
}
//==== Reading sort.stdout ====
const sortOut = Readable.toWeb(
.stdout.setEncoding('utf-8'));
sort.equal(
assertawait readableStreamToString(sortOut),
'apple\nbanana\ncherry\n'
; )
echoOut
等 ReadableStreams 是非同步可迭代的。這就是為什麼我們可以使用 for-await-of
迴圈來讀取它們的區塊(串流資料的片段)。有關更多資訊,請參閱 §10「在 Node.js 上使用 Web 串流」。
有三大類不成功的退出
以下程式碼示範如果無法產生子程序會發生什麼事。在這個案例中,原因是 shell 的路徑沒有指向一個可執行檔 (A 行)。
import {spawn} from 'node:child_process';
const childProcess = spawn(
'echo hello',
{stdio: ['inherit', 'inherit', 'pipe'],
shell: '/bin/does-not-exist', // (A)
};
).on('error', (err) => { // (B)
childProcess.equal(
assert.toString(),
err'Error: spawn /bin/does-not-exist ENOENT'
;
); })
這是我們第一次使用事件來處理子程序。在 B 行,我們註冊一個事件監聽器來監聽 'error'
事件。子程序在目前的程式碼片段執行完後才會啟動。這有助於防止競爭條件:當我們開始監聽時,我們可以確定事件尚未發出。
如果 shell 程式碼包含錯誤,我們不會收到 'error'
事件 (B 行),我們會收到一個退出碼非零的 'exit'
事件 (A 行)
import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
const childProcess = spawn(
'does-not-exist',
{stdio: ['inherit', 'inherit', 'pipe'],
shell: true,
};
).on('exit',
childProcessasync (exitCode, signalCode) => { // (A)
.equal(exitCode, 127);
assert.equal(signalCode, null);
assertconst stderr = Readable.toWeb(
.stderr.setEncoding('utf-8'));
childProcess.equal(
assertawait readableStreamToString(stderr),
'/bin/sh: does-not-exist: command not found\n'
;
)
};
).on('error', (err) => { // (B)
childProcessconsole.error('We never get here!');
; })
如果一個程序在 Unix 上被終止,退出碼會是 null
(C 行),而訊號碼會是一個字串 (D 行)
import {Readable} from 'node:stream';
import {spawn} from 'node:child_process';
const childProcess = spawn(
'kill $$', // (A)
{stdio: ['inherit', 'inherit', 'pipe'],
shell: true,
};
)console.log(childProcess.pid); // (B)
.on('exit', async (exitCode, signalCode) => {
childProcess.equal(exitCode, null); // (C)
assert.equal(signalCode, 'SIGTERM'); // (D)
assertconst stderr = Readable.toWeb(
.stderr.setEncoding('utf-8'));
childProcess.equal(
assertawait readableStreamToString(stderr),
'' // (E)
;
); })
請注意,沒有錯誤輸出 (E 行)。
除了子程序自行終止 (A 行) 之外,我們也可以暫停它一段較長的時間,並透過我們在 B 行記錄的程序 ID 手動終止它。
如果我們在 Windows 上終止一個子程序會發生什麼事?
exitCode
會是 1
。signalCode
會是 null
。有時候我們只想要等到一個指令執行完畢。這可以使用事件或 Promise 來達成。
import * as fs from 'node:fs';
import {spawn} from 'node:child_process';
const childProcess = spawn(
`(echo first && echo second) > tmp-file.txt`,
{shell: true,
stdio: 'inherit',
};
).on('exit', (exitCode, signalCode) => { // (A)
childProcess.equal(exitCode, 0);
assert.equal(signalCode, null);
assert.equal(
assert.readFileSync('tmp-file.txt', {encoding: 'utf-8'}),
fs'first\nsecond\n'
;
); })
我們使用標準的 Node.js 事件模式,並註冊一個 'exit'
事件的監聽器 (A 行)。
import * as fs from 'node:fs';
import {spawn} from 'node:child_process';
const childProcess = spawn(
`(echo first && echo second) > tmp-file.txt`,
{shell: true,
stdio: 'inherit',
};
)
const {exitCode, signalCode} = await onExit(childProcess); // (A)
.equal(exitCode, 0);
assert.equal(signalCode, null);
assert.equal(
assert.readFileSync('tmp-file.txt', {encoding: 'utf-8'}),
fs'first\nsecond\n'
; )
我們在 A 行使用的輔助函式 onExit()
會傳回一個 Promise,如果發出 'exit'
事件,這個 Promise 就會完成
export function onExit(eventEmitter) {
return new Promise((resolve, reject) => {
.once('exit', (exitCode, signalCode) => {
eventEmitterif (exitCode === 0) { // (B)
resolve({exitCode, signalCode});
else {
} reject(new Error(
`Non-zero exit: code ${exitCode}, signal ${signalCode}`));
};
}).once('error', (err) => { // (C)
eventEmitterreject(err);
;
});
}) }
如果 eventEmitter
失敗,傳回的 Promise 會被拒絕,而 await
會在 A 行擲出一個例外。onExit()
處理兩種失敗
exitCode
不是零 (B 行)。這會發生在
exitCode
會大於零。exitCode
會是 null
,而 signalCode
會是非空值。
'error'
事件 (C 行)。如果無法產生子程序,就會發生這種情況。在這個範例中,我們使用 AbortController 來終止一個 shell 指令
import {spawn} from 'node:child_process';
const abortController = new AbortController(); // (A)
const childProcess = spawn(
`echo Hello`,
{stdio: 'inherit',
shell: true,
signal: abortController.signal, // (B)
};
).on('error', (err) => {
childProcess.equal(
assert.toString(),
err'AbortError: The operation was aborted'
;
);
}).abort(); // (C) abortController
我們建立一個 AbortController (第 A 行),將其訊號傳遞給 spawn()
(第 B 行),並透過 AbortController 終止 shell 指令 (第 C 行)。
子處理程序會非同步啟動 (在執行目前的程式碼片段之後)。這就是為什麼我們可以在處理程序啟動之前中止,以及為什麼我們在此案例中看不到任何輸出的原因。
.kill()
終止子處理程序在以下範例中,我們透過 .kill()
方法終止子處理程序 (最後一行)
import {spawn} from 'node:child_process';
const childProcess = spawn(
`echo Hello`,
{stdio: 'inherit',
shell: true,
};
).on('exit', (exitCode, signalCode) => {
childProcess.equal(exitCode, null);
assert.equal(signalCode, 'SIGTERM');
assert;
}).kill(); // default argument value: 'SIGTERM' childProcess
我們再次在子處理程序啟動之前 (非同步地!) 將其終止,且沒有任何輸出。
spawnSync()
spawnSync(
: string,
command?: Array<string>,
args?: Object
options: Object )
spawnSync()
是 spawn()
的同步版本,它會等到子處理程序結束後才同步地傳回一個物件。
參數大部分與 spawn()
的參數 相同。options
有幾個額外的屬性,例如
.input: 字串 | TypedArray | DataView
.encoding: 字串
(預設值:'buffer'
)函式會傳回一個物件。其最有趣的屬性是
.stdout: Buffer | 字串
.stderr: Buffer | 字串
.status: 數字 | null
null
。結束代碼或訊號代碼其中之一會是非 null。.signal: 字串 | null
null
。結束代碼或訊號代碼其中之一會是非 null。.error?: 錯誤
使用非同步的 spawn()
時,子處理程序會並行執行,而且我們可以透過串流讀取標準 I/O。相反地,同步的 spawnSync()
會收集串流的內容並同步地傳回給我們 (請參閱下一個小節)。
使用同步的 spawnSync()
時,指令的子處理程序會同步啟動。以下程式碼示範了這一點
import {spawnSync} from 'node:child_process';
spawnSync(
'echo', ['Command starts'],
{stdio: 'inherit',
shell: true,
};
)console.log('After spawnSync()');
輸出結果為
Command starts
After spawnSync()
以下程式碼示範如何讀取標準輸出
import {spawnSync} from 'node:child_process';
const result = spawnSync(
`echo rock && echo paper && echo scissors`,
{stdio: ['ignore', 'pipe', 'inherit'], // (A)
encoding: 'utf-8', // (B)
shell: true,
};
)console.log(result);
.equal(
assert.stdout, // (C)
result'rock\npaper\nscissors\n'
;
).equal(result.stderr, null); // (D) assert
在 A 行,我們使用 options.stdio
告訴 spawnSync()
我們只對標準輸出有興趣。我們忽略標準輸入,並將標準錯誤輸出導向父程序。
因此,我們只取得標準輸出的結果屬性 (C 行),而標準錯誤輸出的屬性為 null
(D 行)。
由於我們無法存取 spawnSync()
內部用來處理子程序標準 I/O 的串流,因此我們透過 options.encoding
(B 行) 告訴它要使用哪種編碼。
我們可以透過選項屬性 .input
(A 行) 將資料傳送至子程序的標準輸入串流
import {spawnSync} from 'node:child_process';
const result = spawnSync(
`sort`,
{stdio: ['pipe', 'pipe', 'inherit'],
encoding: 'utf-8',
input: 'Cherry\nApple\nBanana\n', // (A)
};
).equal(
assert.stdout,
result'Apple\nBanana\nCherry\n'
; )
有三大類失敗的退出 (當退出碼不為零時)
如果產生失敗,spawn()
會發出 'error'
事件。相反地,spawnSync()
會將 result.error
設為錯誤物件
import {spawnSync} from 'node:child_process';
const result = spawnSync(
'echo hello',
{stdio: ['ignore', 'inherit', 'pipe'],
encoding: 'utf-8',
shell: '/bin/does-not-exist',
};
).equal(
assert.error.toString(),
result'Error: spawnSync /bin/does-not-exist ENOENT'
; )
如果殼層中發生錯誤,退出碼 result.status
會大於零,而 result.signal
為 null
import {spawnSync} from 'node:child_process';
const result = spawnSync(
'does-not-exist',
{stdio: ['ignore', 'inherit', 'pipe'],
encoding: 'utf-8',
shell: true,
};
).equal(result.status, 127);
assert.equal(result.signal, null);
assert.equal(
assert.stderr, '/bin/sh: does-not-exist: command not found\n'
result; )
如果子程序在 Unix 上被終止,result.signal
會包含訊號名稱,而 result.status
為 null
import {spawnSync} from 'node:child_process';
const result = spawnSync(
'kill $$',
{stdio: ['ignore', 'inherit', 'pipe'],
encoding: 'utf-8',
shell: true,
};
)
.equal(result.status, null);
assert.equal(result.signal, 'SIGTERM');
assert.equal(result.stderr, ''); // (A) assert
請注意,沒有輸出傳送至標準錯誤串流 (A 行)。
如果我們在 Windows 上終止子程序
result.status
為 1result.signal
為 null
result.stderr
為 ''
spawn()
的非同步輔助函數在本節中,我們將探討模組 node:child_process
中兩個基於 spawn()
的非同步函數
exec()
execFile()
我們在本章節中忽略 fork()
。引用 Node.js 文件
fork()
產生新的 Node.js 程序,並呼叫指定的模組,並建立 IPC 通訊管道,允許在父代和子代之間傳送訊息。
exec()
exec(
: string,
command?: Object,
options?: (error, stdout, stderr) => void
callback: ChildProcess )
exec()
在新產生的 shell 中執行命令。與 spawn()
的主要差異在於
exec()
也透過 callback 傳送結果:錯誤物件或 stdout 和 stderr 的內容。spawn()
只有在無法產生子程序時才會發出 'error'
事件。其他兩個失敗會透過結束代碼和(在 Unix 上)訊號代碼處理。args
參數。options.shell
的預設值為 true
。import {exec} from 'node:child_process';
const childProcess = exec(
'echo Hello',
, stdout, stderr) => {
(errorif (error) {
console.error('error: ' + error.toString());
return;
}console.log('stdout: ' + stdout); // 'stdout: Hello\n'
console.error('stderr: ' + stderr); // 'stderr: '
}; )
exec()
可以透過 util.promisify()
轉換為基於 Promise 的函式
{stdout, stderr}
error
參數相同的值,但多了兩個屬性:.stdout
和 .stderr
。import * as util from 'node:util';
import * as child_process from 'node:child_process';
const execAsync = util.promisify(child_process.exec);
try {
const resultPromise = execAsync('echo Hello');
const {childProcess} = resultPromise;
const obj = await resultPromise;
console.log(obj); // { stdout: 'Hello\n', stderr: '' }
catch (err) {
} console.error(err);
}
execFile()
execFile(file, args?, options?, callback?): ChildProcess
運作方式類似於 exec()
,但有以下差異
args
參數。options.shell
的預設值為 false
。與 exec()
一樣,execFile()
可以透過 util.promisify()
轉換為基於 Promise 的函式。
spawnAsync()
的同步輔助函式execSync()
execSync(
: string,
command?: Object
options: Buffer | string )
execSync()
在新的子程序中執行命令,並同步等待該程序結束。與 spawnSync()
的主要差異在於
spawnSync()
的結果只有在無法產生子程序時才會有一個 .error
屬性。其他兩個失敗會透過結束代碼和(在 Unix 上)訊號代碼處理。args
參數。options.shell
的預設值為 true
。import {execSync} from 'node:child_process';
try {
const stdout = execSync('echo Hello');
console.log('stdout: ' + stdout); // 'stdout: Hello\n'
catch (err) {
} console.error('Error: ' + err.toString());
}
execFileSync()
execFileSync(file, args?, options?): Buffer | string
作用類似於 execSync()
,但有以下差異
args
參數。options.shell
的預設值為 false
。tinysh 由 Anton Medvedev 製作,是一個有助於產生 shell 命令的小型函式庫,例如
import sh from 'tinysh';
console.log(sh.ls('-l'));
console.log(sh.cat('README.md'));
我們可以使用 .call()
傳遞物件作為 this
來覆寫預設選項
.tee.call({input: 'Hello, world!'}, 'file.txt'); sh
我們可以使用任何屬性名稱,而 tinysh 會執行具有該名稱的 shell 命令。它透過 代理 來達成此壯舉。這是實際函式庫的略微修改版本
import {execFileSync} from 'node:child_process';
const sh = new Proxy({}, {
get: (_, bin) => function (...args) { // (A)
return execFileSync(bin, args,
{encoding: 'utf-8',
shell: true,
...this // (B)
};
),
}; })
在 A 行中,我們可以看到,如果我們從 sh
取得名稱為 bin
的屬性,則會傳回一個函式,該函式會呼叫 execFileSync()
並使用 bin
作為第一個引數。
在 B 行中散佈 this
使我們能夠透過 .call()
指定選項。預設值會先出現,以便它們可以透過 this
來覆寫。
在 Windows 上使用 函式庫 node-powershell 如下所示
import { PowerShell } from 'node-powershell';
.$`echo "hello from PowerShell"`; PowerShell
'node:child_process'
的函式一般限制
spawn()
在這種情況下較為簡單,因為它沒有傳遞錯誤和標準 I/O 內容的回呼。exec()
和 execFile()
spawnSync()
、execSync()
、execFileSync()
非同步函式 - 在 spawn()
和 exec()
或 execFile()
之間進行選擇
exec()
和 execFile()
有兩個好處
spawn()
。它的簽章較為簡單,沒有(可選的)回呼。同步函式 - 在 spawnSync()
和 execSync()
或 execFileSync()
之間進行選擇
execSync()
和 execFileSync()
有兩個特色
execSync()
和 execFileSync()
透過其回傳值和例外狀況提供的更多資訊,請選擇 spawnSync()
。在 exec()
和 execFile()
之間做選擇(在 execSync()
和 execFileSync()
之間做選擇時套用相同的論點)
exec()
中的 options.shell
預設為 true
,但 execFile()
中的 options.shell
預設為 false
。execFile()
支援 args
,但 exec()
不支援。