execa
Process execution for humans
Process execution for humans
Execa runs commands in your script, application or library. Unlike shells, it is optimized for programmatic usage. Built on top of the child_process
core module.
One of the maintainers @ehmicky is looking for a remote full-time position. Specialized in Node.js back-ends and CLIs, he led Netlify Build, Plugins and Configuration for 2.5 years. Feel free to contact him on his website or on LinkedIn!
-
Simple syntax: promises and template strings, like
zx
. - Script interface.
- No escaping nor quoting needed. No risk of shell injection.
- Execute locally installed binaries without
npx
. - Improved Windows support: shebangs,
PATHEXT
, and more. - Detailed errors and verbose mode, for debugging.
- Pipe multiple subprocesses better than in shells: retrieve intermediate results, use multiple sources/destinations, unpipe.
- Split the output into text lines, or iterate progressively over them.
- Strip unnecessary newlines.
- Get interleaved output from
stdout
andstderr
similar to what is printed on the terminal. - Retrieve the output programmatically and print it on the console at the same time.
- Transform or filter the input and output with simple functions.
- Redirect the input and output from/to files, strings,
Uint8Array
s, iterables or objects. - Pass Node.js streams or web streams to subprocesses, or convert subprocesses to a stream.
- Ensure subprocesses exit even when they intercept termination signals, or when the current process ends abruptly.
npm install execa
Execution:
▶️ Basic execution- 💬 Escaping/quoting
- 💻 Shell
- 📜 Scripts
- 🐢 Node.js files
- 🌐 Environment
- ❌ Errors
- 🏁 Termination
Input/output:
- 🎹 Input
- 📢 Output
- 📃 Text lines
- 🤖 Binary data
- 🧙 Transforms
Advanced usage:
- 🔀 Piping multiple subprocesses
- ⏳️ Streams
- 📞 Inter-process communication
- 🐛 Debugging
- 📎 Windows
- 🔍 Difference with Bash and zx
- 🤓 TypeScript
- 📔 API reference
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);
// Similar to: npm run build > output.txt
await execa({stdout: {file: './output.txt'}})`npm run build`;
const {stdout} = await execa({input: getInputString()})`sort`;
console.log(stdout);
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`;
const response = await fetch('https://example.com');
await execa({stdin: response.body})`sort`;
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'),
);
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`;
- gulp-execa - Gulp plugin for Execa
- nvexeca - Run Execa using any Node.js version