sindresorhus/execa · 文件 下载 ZIP
文件最后提交记录最后更新时间
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈
面向人类的进程执行
Execa 在你的脚本、应用程序或库中运行命令。与 shell 不同,它针对编程使用进行了优化。构建于 child_process 核心模块之上。
特性
- 简洁的语法:承诺和模板字符串,类似于
zx。 - 脚本接口。
- 无需转义或引号。无 shell 注入风险。
- 执行本地安装的二进制文件,无需
npx。 - 改进的Windows 支持:shebangs、
PATHEXT、优雅终止、以及更多。 - 详细的错误、详细模式和自定义日志,用于调试。
- 比 shell 更好地管道多个子进程:获取中间结果,使用多个源/目标,取消管道。
- 将输出拆分为文本行,或迭代处理它们。
- 去除不必要的新行。
- 向子进程传递任何输入:文件、字符串、
Uint8Arrays、可迭代对象、对象以及几乎任何其他类型。 - 从子进程返回几乎任何类型,或将其重定向到文件。
- 获取来自
stdout和stderr的交错输出,类似于终端上打印的内容。 - 以编程方式获取输出并打印到控制台。
- 使用简单的函数转换或过滤输入和输出。
- 向子进程传递Node.js 流或Web 流,或将子进程转换为流。
- 与子进程交换消息。
- 确保子进程退出,即使它们拦截终止信号,或当前进程突然结束。
安装
npm install execa
文档
执行:
输入/输出:
高级用法:
- 🔀 管道连接多个子进程
- ⏳️ 流
- 📞 进程间通信
- 🐛 调试
- 📎 Windows
- 🔍 与 Bash 和 zx 的区别
- 🐭 小型包
- 🤓 TypeScript
- 📔 API 参考
示例
执行
简单语法
import {execa} from 'execa';
const {stdout} = await execa`npm run build`;
// Print command's output
console.log(stdout);
脚本
import {$} from 'execa';
const {stdout: name} = await $`cat package.json`.pipe`grep name`;
console.log(name);
const branch = await $`git branch --show-current`;
await $`dep deploy --branch=${branch}`;
await Promise.all([
$`sleep 1`,
$`sleep 2`,
$`sleep 3`,
]);
const directoryName = 'foo bar';
await $`mkdir /tmp/${directoryName}`;
本地二进制文件
$ npm install -D eslint
await execa({preferLocal: true})`eslint`;
管道连接多个子进程
const {stdout, pipedFrom} = await execa`npm run build`
.pipe`sort`
.pipe`head -n 2`;
// Output of `npm run build | sort | head -n 2`
console.log(stdout);
// Output of `npm run build | sort`
console.log(pipedFrom[0].stdout);
// Output of `npm run build`
console.log(pipedFrom[0].pipedFrom[0].stdout);
输入/输出
交错输出
const {all} = await execa({all: true})`npm run build`;
// stdout + stderr, interleaved
console.log(all);
编程 + 终端输出
const {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`;
// stdout is also printed to the terminal
console.log(stdout);
简单输入
const getInputString = () => { /* ... */ };
const {stdout} = await execa({input: getInputString()})`sort`;
console.log(stdout);
文件输入
// Similar to: npm run build < input.txt
await execa({stdin: {file: 'input.txt'}})`npm run build`;
文件输出
// Similar to: npm run build > output.txt
await execa({stdout: {file: 'output.txt'}})`npm run build`;
拆分为文本行
const {stdout} = await execa({lines: true})`npm run build`;
// Print first 10 lines
console.log(stdout.slice(0, 10).join('\n'));
流式传输
逐行迭代文本
for await (const line of execa`npm run build`) {
if (line.includes('WARN')) {
console.warn(line);
}
}
转换/过滤输出
let count = 0;
// Filter out secret lines, then prepend the line number
const transform = function * (line) {
if (!line.includes('secret')) {
yield `[${count++}] ${line}`;
}
};
await execa({stdout: transform})`npm run build`;
Web 流
const response = await fetch('https://example.com');
await execa({stdin: response.body})`sort`;
转换为 Duplex 流
import {execa} from 'execa';
import {pipeline} from 'node:stream/promises';
import {createReadStream, createWriteStream} from 'node:fs';
await pipeline(
createReadStream('./input.txt'),
execa`node ./transform.js`.duplex(),
createWriteStream('./output.txt'),
);
IPC
交换消息
// parent.js
import {execaNode} from 'execa';
const subprocess = execaNode`child.js`;
await subprocess.sendMessage('Hello from parent');
const message = await subprocess.getOneMessage();
console.log(message); // 'Hello from child'
// child.js
import {getOneMessage, sendMessage} from 'execa';
const message = await getOneMessage(); // 'Hello from parent'
const newMessage = message.replace('parent', 'child'); // 'Hello from child'
await sendMessage(newMessage);
任意输入类型
// main.js
import {execaNode} from 'execa';
const ipcInput = [
{task: 'lint', ignore: /test\.js/},
{task: 'copy', files: new Set(['main.js', 'index.js']),
}];
await execaNode({ipcInput})`build.js`;
// build.js
import {getOneMessage} from 'execa';
const ipcInput = await getOneMessage();
任意输出类型
// main.js
import {execaNode} from 'execa';
const {ipcOutput} = await execaNode`build.js`;
console.log(ipcOutput[0]); // {kind: 'start', timestamp: date}
console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date}
// build.js
import {sendMessage} from 'execa';
const runBuild = () => { /* ... */ };
await sendMessage({kind: 'start', timestamp: new Date()});
await runBuild();
await sendMessage({kind: 'stop', timestamp: new Date()});
优雅终止
// main.js
import {execaNode} from 'execa';
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, 5000);
await execaNode({
cancelSignal: controller.signal,
gracefulCancel: true,
})`build.js`;
// build.js
import {getCancelSignal} from 'execa';
const cancelSignal = await getCancelSignal();
const url = 'https://example.com/build/info';
const response = await fetch(url, {signal: cancelSignal});
调试
详细错误
import {execa, ExecaError} from 'execa';
try {
await execa`unknown command`;
} catch (error) {
if (error instanceof ExecaError) {
console.log(error);
}
/*
ExecaError: Command failed with ENOENT: unknown command
spawn unknown ENOENT
at ...
at ... {
shortMessage: 'Command failed with ENOENT: unknown command\nspawn unknown ENOENT',
originalMessage: 'spawn unknown ENOENT',
command: 'unknown command',
escapedCommand: 'unknown command',
cwd: '/path/to/cwd',
durationMs: 28.217566,
failed: true,
timedOut: false,
isCanceled: false,
isTerminated: false,
isMaxBuffer: false,
code: 'ENOENT',
stdout: '',
stderr: '',
stdio: [undefined, '', ''],
pipedFrom: []
[cause]: Error: spawn unknown ENOENT
at ...
at ... {
errno: -2,
code: 'ENOENT',
syscall: 'spawn unknown',
path: 'unknown',
spawnargs: [ 'command' ]
}
}
*/
}
详细模式
await execa`npm run build`;
await execa`npm run test`;
自定义日志
import {execa as execa_} from 'execa';
import {createLogger, transports} from 'winston';
// Log to a file using Winston
const transport = new transports.File({filename: 'logs.txt'});
const logger = createLogger({transports: [transport]});
const LOG_LEVELS = {
command: 'info',
output: 'verbose',
ipc: 'verbose',
error: 'error',
duration: 'info',
};
const execa = execa_({
verbose(verboseLine, {message, ...verboseObject}) {
const level = LOG_LEVELS[verboseObject.type];
logger[level](message, verboseObject);
},
});
await execa`npm run build`;
await execa`npm run test`;
相关
- nano-spawn - 类似 Execa 但更小
- gulp-execa - Execa 的 Gulp 插件
- nvexeca - 使用任意 Node.js 版本运行 Execa