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
转换为 Web 流(ReadableStream
的实例)。这在 §10 “在 Node.js 上使用 Web 流” 中有解释。Readable
始终在示例中导入。
异步函数 readableStreamToString()
使用可读的 Web 流并返回一个字符串(包装在 Promise 中)。这在 关于 Web 流的章节 中有解释。假设此函数在示例中可用。
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: string | URL
.stdio: Array<string|Stream>|string
.env: Object
(默认值:process.env
)查看 process.env
(例如在 Node.js REPL 中)以查看存在哪些变量。
我们可以使用展开来非破坏性地覆盖现有变量——或者如果它尚不存在则创建它
env: {...process.env, MY_VAR: 'Hi!'}} {
.signal: AbortSignal
ac
,我们可以将 ac.signal
传递给 spawn()
并通过 ac.abort()
终止子进程。这将在 本章后面 进行演示。.timeout: number
.timeout
毫秒,则将其终止。options.stdio
子进程的每个标准 I/O 流都有一个数字 ID,即所谓的*文件描述符*
可能还有更多文件描述符,但这很少见。
options.stdio
配置子进程的流是否以及如何管道传输到父进程中的流。它可以是一个数组,其中每个元素配置与其索引相等的文件描述符。以下值可以用作数组元素
'pipe'
:
childProcess.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: number | null
null
表示进程尚未退出。.signalCode: string | null
null
。有关更多信息,请参阅下方方法 .kill()
的说明。.stdin
.stdout
.stderr
.pid: number | undefined
.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'
; )
ReadableStreams(例如 echoOut
)是异步可迭代的。这就是为什么我们可以使用 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: string | TypedArray | DataView
.encoding: string
(默认值:'buffer'
)该函数返回一个对象。它最有趣的属性是
.stdout: Buffer | string
.stderr: Buffer | string
.status: number | null
null
。退出代码和信号代码其中之一为非空。.signal: string | null
null
。退出代码和信号代码其中之一为非空。.error?: 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'
; )
如果 shell 中发生错误,则退出代码 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()
还通过回调传递结果:错误对象或标准输出和标准错误的内容。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
。Anton Medvedev 开发的 tinysh 是一个小型库,可以帮助生成 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()
之间进行选择)
options.shell
的默认值在 exec()
中为 true
,但在 execFile()
中为 false
。execFile()
支持 args
,而 exec()
不支持。