{"id":698,"url":"https://github.com/sindresorhus/execa","last_synced_at":"2025-05-12T18:41:25.397Z","repository":{"id":38360264,"uuid":"47475908","full_name":"sindresorhus/execa","owner":"sindresorhus","description":"Process execution for humans","archived":false,"fork":false,"pushed_at":"2025-04-04T19:29:48.000Z","size":2440,"stargazers_count":7120,"open_issues_count":14,"forks_count":236,"subscribers_count":41,"default_branch":"main","last_synced_at":"2025-05-05T16:02:21.772Z","etag":null,"topics":["binary","child-process","exec","execute","javascript","nodejs","shell","spawn","spawned-processes","streams"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sindresorhus.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"sindresorhus","open_collective":"sindresorhus","buy_me_a_coffee":"sindresorhus","custom":"https://sindresorhus.com/donate"}},"created_at":"2015-12-05T22:57:03.000Z","updated_at":"2025-05-03T20:48:32.000Z","dependencies_parsed_at":"2023-01-31T00:46:13.462Z","dependency_job_id":"91bec470-e5a8-4019-aa6d-8b23fb54cf56","html_url":"https://github.com/sindresorhus/execa","commit_stats":{"total_commits":799,"total_committers":63,"mean_commits":"12.682539682539682","dds":0.3028785982478097,"last_synced_commit":"c8cff27a47b6e6f1cfbfec2bf7fa9dcd08cefed1"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sindresorhus%2Fexeca","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sindresorhus%2Fexeca/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sindresorhus%2Fexeca/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sindresorhus%2Fexeca/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sindresorhus","download_url":"https://codeload.github.com/sindresorhus/execa/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252963492,"owners_count":21832512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary","child-process","exec","execute","javascript","nodejs","shell","spawn","spawned-processes","streams"],"created_at":"2024-01-05T20:15:29.118Z","updated_at":"2025-05-12T18:41:25.371Z","avatar_url":"https://github.com/sindresorhus.png","language":"JavaScript","readme":"\u003cpicture\u003e\n\t\u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"media/logo_dark.svg\"\u003e\n\t\u003cimg alt=\"execa logo\" src=\"media/logo.svg\" width=\"400\"\u003e\n\u003c/picture\u003e\n\u003cbr\u003e\n\n[![Coverage Status](https://codecov.io/gh/sindresorhus/execa/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/execa)\n\n\u003e Process execution for humans\n\n\u003cbr\u003e\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\t\u003cp\u003e\n\t\t\u003cp\u003e\n\t\t\t\u003csup\u003e\n\t\t\t\t\u003ca href=\"https://github.com/sponsors/sindresorhus\"\u003eSindre's open source work is supported by the community\u003c/a\u003e\n\t\t\t\u003c/sup\u003e\n\t\t\u003c/p\u003e\n\t\t\u003csup\u003eSpecial thanks to:\u003c/sup\u003e\n\t\t\u003cbr\u003e\n\t\t\u003cbr\u003e\n\t\t\u003ca href=\"https://coderabbit.ai?utm_source=sindre\u0026utm_medium=execa\"\u003e\n\t\t\t\u003cimg width=\"300\" src=\"https://sindresorhus.com/assets/thanks/coderabbit-logo.png\" alt=\"CodeRabbit logo\"\u003e\n\t\t\u003c/a\u003e\n\t\t\u003cbr\u003e\n\t\t\u003cbr\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n\u003cbr\u003e\n\nExeca runs commands in your script, application or library. Unlike shells, it is [optimized](docs/bash.md) for programmatic usage. Built on top of the [`child_process`](https://nodejs.org/api/child_process.html) core module.\n\n## Features\n\n- [Simple syntax](#simple-syntax): promises and [template strings](docs/execution.md#template-string-syntax), like [`zx`](docs/bash.md).\n- [Script](#script) interface.\n- [No escaping](docs/escaping.md) nor quoting needed. No risk of shell injection.\n- Execute [locally installed binaries](#local-binaries) without `npx`.\n- Improved [Windows support](docs/windows.md): [shebangs](docs/windows.md#shebang), [`PATHEXT`](https://ss64.com/nt/path.html#pathext), [graceful termination](#graceful-termination), [and more](https://github.com/moxystudio/node-cross-spawn?tab=readme-ov-file#why).\n- [Detailed errors](#detailed-error), [verbose mode](#verbose-mode) and [custom logging](#custom-logging), for [debugging](docs/debugging.md).\n- [Pipe multiple subprocesses](#pipe-multiple-subprocesses) better than in shells: retrieve [intermediate results](docs/pipe.md#result), use multiple [sources](docs/pipe.md#multiple-sources-1-destination)/[destinations](docs/pipe.md#1-source-multiple-destinations), [unpipe](docs/pipe.md#unpipe).\n- [Split](#split-into-text-lines) the output into text lines, or [iterate](#iterate-over-text-lines) progressively over them.\n- Strip [unnecessary newlines](docs/lines.md#newlines).\n- Pass any [input](docs/input.md) to the subprocess: [files](#file-input), [strings](#simple-input), [`Uint8Array`s](docs/binary.md#binary-input), [iterables](docs/streams.md#iterables-as-input), [objects](docs/transform.md#object-mode) and almost any [other type](#any-input-type).\n- Return [almost any type](#any-output-type) from the subprocess, or redirect it to [files](#file-output).\n- Get [interleaved output](#interleaved-output) from `stdout` and `stderr` similar to what is printed on the terminal.\n- Retrieve the output [programmatically and print it](#programmatic--terminal-output) on the console at the same time.\n- [Transform or filter](#transformfilter-output) the input and output with [simple functions](docs/transform.md).\n- Pass [Node.js streams](docs/streams.md#nodejs-streams) or [web streams](#web-streams) to subprocesses, or [convert](#convert-to-duplex-stream) subprocesses to [a stream](docs/streams.md#converting-a-subprocess-to-a-stream).\n- [Exchange messages](#exchange-messages) with the subprocess.\n- Ensure subprocesses exit even when they [intercept termination signals](docs/termination.md#forceful-termination), or when the current process [ends abruptly](docs/termination.md#current-process-exit).\n\n## Install\n\n```sh\nnpm install execa\n```\n\n## Documentation\n\nExecution:\n- ▶️ [Basic execution](docs/execution.md)\n- 💬 [Escaping/quoting](docs/escaping.md)\n- 💻 [Shell](docs/shell.md)\n- 📜 [Scripts](docs/scripts.md)\n- 🐢 [Node.js files](docs/node.md)\n- 🌐 [Environment](docs/environment.md)\n- ❌ [Errors](docs/errors.md)\n- 🏁 [Termination](docs/termination.md)\n\nInput/output:\n- 🎹 [Input](docs/input.md)\n- 📢 [Output](docs/output.md)\n- 📃 [Text lines](docs/lines.md)\n- 🤖 [Binary data](docs/binary.md)\n- 🧙 [Transforms](docs/transform.md)\n\nAdvanced usage:\n- 🔀 [Piping multiple subprocesses](docs/pipe.md)\n- ⏳️ [Streams](docs/streams.md)\n- 📞 [Inter-process communication](docs/ipc.md)\n- 🐛 [Debugging](docs/debugging.md)\n- 📎 [Windows](docs/windows.md)\n- 🔍 [Difference with Bash and zx](docs/bash.md)\n- 🐭 [Small packages](docs/small.md)\n- 🤓 [TypeScript](docs/typescript.md)\n- 📔 [API reference](docs/api.md)\n\n## Examples\n\n### Execution\n\n#### Simple syntax\n\n```js\nimport {execa} from 'execa';\n\nconst {stdout} = await execa`npm run build`;\n// Print command's output\nconsole.log(stdout);\n```\n\n#### Script\n\n```js\nimport {$} from 'execa';\n\nconst {stdout: name} = await $`cat package.json`.pipe`grep name`;\nconsole.log(name);\n\nconst branch = await $`git branch --show-current`;\nawait $`dep deploy --branch=${branch}`;\n\nawait Promise.all([\n\t$`sleep 1`,\n\t$`sleep 2`,\n\t$`sleep 3`,\n]);\n\nconst directoryName = 'foo bar';\nawait $`mkdir /tmp/${directoryName}`;\n```\n\n#### Local binaries\n\n```sh\n$ npm install -D eslint\n```\n\n```js\nawait execa({preferLocal: true})`eslint`;\n```\n\n#### Pipe multiple subprocesses\n\n```js\nconst {stdout, pipedFrom} = await execa`npm run build`\n\t.pipe`sort`\n\t.pipe`head -n 2`;\n\n// Output of `npm run build | sort | head -n 2`\nconsole.log(stdout);\n// Output of `npm run build | sort`\nconsole.log(pipedFrom[0].stdout);\n// Output of `npm run build`\nconsole.log(pipedFrom[0].pipedFrom[0].stdout);\n```\n\n### Input/output\n\n#### Interleaved output\n\n```js\nconst {all} = await execa({all: true})`npm run build`;\n// stdout + stderr, interleaved\nconsole.log(all);\n```\n\n#### Programmatic + terminal output\n\n```js\nconst {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`;\n// stdout is also printed to the terminal\nconsole.log(stdout);\n```\n\n#### Simple input\n\n```js\nconst getInputString = () =\u003e { /* ... */ };\nconst {stdout} = await execa({input: getInputString()})`sort`;\nconsole.log(stdout);\n```\n\n#### File input\n\n```js\n// Similar to: npm run build \u003c input.txt\nawait execa({stdin: {file: 'input.txt'}})`npm run build`;\n```\n\n#### File output\n\n```js\n// Similar to: npm run build \u003e output.txt\nawait execa({stdout: {file: 'output.txt'}})`npm run build`;\n```\n\n#### Split into text lines\n\n```js\nconst {stdout} = await execa({lines: true})`npm run build`;\n// Print first 10 lines\nconsole.log(stdout.slice(0, 10).join('\\n'));\n```\n\n### Streaming\n\n#### Iterate over text lines\n\n```js\nfor await (const line of execa`npm run build`) {\n\tif (line.includes('WARN')) {\n\t\tconsole.warn(line);\n\t}\n}\n```\n\n#### Transform/filter output\n\n```js\nlet count = 0;\n\n// Filter out secret lines, then prepend the line number\nconst transform = function * (line) {\n\tif (!line.includes('secret')) {\n\t\tyield `[${count++}] ${line}`;\n\t}\n};\n\nawait execa({stdout: transform})`npm run build`;\n```\n\n#### Web streams\n\n```js\nconst response = await fetch('https://example.com');\nawait execa({stdin: response.body})`sort`;\n```\n\n#### Convert to Duplex stream\n\n```js\nimport {execa} from 'execa';\nimport {pipeline} from 'node:stream/promises';\nimport {createReadStream, createWriteStream} from 'node:fs';\n\nawait pipeline(\n\tcreateReadStream('./input.txt'),\n\texeca`node ./transform.js`.duplex(),\n\tcreateWriteStream('./output.txt'),\n);\n```\n\n### IPC\n\n#### Exchange messages\n\n```js\n// parent.js\nimport {execaNode} from 'execa';\n\nconst subprocess = execaNode`child.js`;\nawait subprocess.sendMessage('Hello from parent');\nconst message = await subprocess.getOneMessage();\nconsole.log(message); // 'Hello from child'\n```\n\n```js\n// child.js\nimport {getOneMessage, sendMessage} from 'execa';\n\nconst message = await getOneMessage(); // 'Hello from parent'\nconst newMessage = message.replace('parent', 'child'); // 'Hello from child'\nawait sendMessage(newMessage);\n```\n\n#### Any input type\n\n```js\n// main.js\nimport {execaNode} from 'execa';\n\nconst ipcInput = [\n\t{task: 'lint', ignore: /test\\.js/},\n\t{task: 'copy', files: new Set(['main.js', 'index.js']),\n}];\nawait execaNode({ipcInput})`build.js`;\n```\n\n```js\n// build.js\nimport {getOneMessage} from 'execa';\n\nconst ipcInput = await getOneMessage();\n```\n\n#### Any output type\n\n```js\n// main.js\nimport {execaNode} from 'execa';\n\nconst {ipcOutput} = await execaNode`build.js`;\nconsole.log(ipcOutput[0]); // {kind: 'start', timestamp: date}\nconsole.log(ipcOutput[1]); // {kind: 'stop', timestamp: date}\n```\n\n```js\n// build.js\nimport {sendMessage} from 'execa';\n\nconst runBuild = () =\u003e { /* ... */ };\n\nawait sendMessage({kind: 'start', timestamp: new Date()});\nawait runBuild();\nawait sendMessage({kind: 'stop', timestamp: new Date()});\n```\n\n#### Graceful termination\n\n```js\n// main.js\nimport {execaNode} from 'execa';\n\nconst controller = new AbortController();\nsetTimeout(() =\u003e {\n\tcontroller.abort();\n}, 5000);\n\nawait execaNode({\n\tcancelSignal: controller.signal,\n\tgracefulCancel: true,\n})`build.js`;\n```\n\n```js\n// build.js\nimport {getCancelSignal} from 'execa';\n\nconst cancelSignal = await getCancelSignal();\nconst url = 'https://example.com/build/info';\nconst response = await fetch(url, {signal: cancelSignal});\n```\n\n### Debugging\n\n#### Detailed error\n\n```js\nimport {execa, ExecaError} from 'execa';\n\ntry {\n\tawait execa`unknown command`;\n} catch (error) {\n\tif (error instanceof ExecaError) {\n\t\tconsole.log(error);\n\t}\n\t/*\n\tExecaError: Command failed with ENOENT: unknown command\n\tspawn unknown ENOENT\n\t\t\tat ...\n\t\t\tat ... {\n\t\tshortMessage: 'Command failed with ENOENT: unknown command\\nspawn unknown ENOENT',\n\t\toriginalMessage: 'spawn unknown ENOENT',\n\t\tcommand: 'unknown command',\n\t\tescapedCommand: 'unknown command',\n\t\tcwd: '/path/to/cwd',\n\t\tdurationMs: 28.217566,\n\t\tfailed: true,\n\t\ttimedOut: false,\n\t\tisCanceled: false,\n\t\tisTerminated: false,\n\t\tisMaxBuffer: false,\n\t\tcode: 'ENOENT',\n\t\tstdout: '',\n\t\tstderr: '',\n\t\tstdio: [undefined, '', ''],\n\t\tpipedFrom: []\n\t\t[cause]: Error: spawn unknown ENOENT\n\t\t\t\tat ...\n\t\t\t\tat ... {\n\t\t\terrno: -2,\n\t\t\tcode: 'ENOENT',\n\t\t\tsyscall: 'spawn unknown',\n\t\t\tpath: 'unknown',\n\t\t\tspawnargs: [ 'command' ]\n\t\t}\n\t}\n\t*/\n}\n```\n\n#### Verbose mode\n\n```js\nawait execa`npm run build`;\nawait execa`npm run test`;\n```\n\n\u003cimg alt=\"execa verbose output\" src=\"media/verbose.png\" width=\"603\"\u003e\n\n#### Custom logging\n\n```js\nimport {execa as execa_} from 'execa';\nimport {createLogger, transports} from 'winston';\n\n// Log to a file using Winston\nconst transport = new transports.File({filename: 'logs.txt'});\nconst logger = createLogger({transports: [transport]});\nconst LOG_LEVELS = {\n\tcommand: 'info',\n\toutput: 'verbose',\n\tipc: 'verbose',\n\terror: 'error',\n\tduration: 'info',\n};\n\nconst execa = execa_({\n\tverbose(verboseLine, {message, ...verboseObject}) {\n\t\tconst level = LOG_LEVELS[verboseObject.type];\n\t\tlogger[level](message, verboseObject);\n\t},\n});\n\nawait execa`npm run build`;\nawait execa`npm run test`;\n```\n\n## Related\n\n- [nano-spawn](https://github.com/sindresorhus/nano-spawn) - Like Execa but [smaller](docs/small.md)\n- [gulp-execa](https://github.com/ehmicky/gulp-execa) - Gulp plugin for Execa\n- [nvexeca](https://github.com/ehmicky/nvexeca) - Run Execa using any Node.js version\n\n## Maintainers\n\n- [Sindre Sorhus](https://github.com/sindresorhus)\n- [@ehmicky](https://github.com/ehmicky)\n","funding_links":["https://github.com/sponsors/sindresorhus","https://opencollective.com/sindresorhus","https://buymeacoffee.com/sindresorhus","https://sindresorhus.com/donate"],"categories":["Node","命令行","目录","JavaScript","Number","Repository","包","Packages","GIT 仓库","进程","Miscellaneous","shell","Node.js",":books: Libraries","Libraries","Works with AVA"],"sub_categories":["文件处理","redux 扩展","其他","Miscellaneous","Shell","macros","脚本执行","杂项","运维\\\u0026DevOps","Terminal","Node"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsindresorhus%2Fexeca","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsindresorhus%2Fexeca","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsindresorhus%2Fexeca/lists"}