戰(zhàn)指南:基于 Promise 與模板字符串的現(xiàn)代化進(jìn)程執(zhí)行庫)
開發(fā)工具【免費(fèi)下載鏈接】execaProcess execution for humans項(xiàng)目地址https://gitcode.com/gh_mirrors/ex/execa點(diǎn)擊查看免費(fèi)下載本指南以 execa 官方 READMEreadme.md為核心骨架結(jié)合倉庫源碼與測試展開。Execa 運(yùn)行你腳本、應(yīng)用或庫中的命令與 shell 不同它專門為編程化使用programmatic usage而優(yōu)化構(gòu)建于 Node.js 核心模塊child_process之上。讀完本文你將掌握 execa 的模板字符串語法、$腳本接口、本地二進(jìn)制執(zhí)行、多進(jìn)程管道、輸入輸出類型轉(zhuǎn)換、IPC 消息通信、優(yōu)雅終止以及調(diào)試與自定義日志等完整實(shí)戰(zhàn)能力。一、項(xiàng)目定位為人類而生的進(jìn)程執(zhí)行Execa 的口號是Process execution for humans。它把 Node.js 底層child_process繁瑣的spawn/exec/fork調(diào)用包裝成簡潔、類型安全、Promise 化的高層 API并針對程序化調(diào)用場景做了大量優(yōu)化這一點(diǎn)在 docs/bash.md 中有專門對比說明。從 package.json 可以看到當(dāng)前倉庫版本為10.0.1要求Node.js 22采用 ESMtype: module規(guī)范通過exports字段暴露typesindex.d.ts與defaultindex.js入口內(nèi)置依賴包括get-stream、npm-run-path、signal-exit、strip-final-newline、yoctocolors等這些依賴分別服務(wù)于流式收集輸出、本地二進(jìn)制路徑解析、退出清理、去換行與彩色輸出等底層能力。二、安裝npm install execa安裝后即可在 ESM 項(xiàng)目中直接導(dǎo)入import {execa} from execa;三、核心特性總覽Execa 的 Features 清單既是能力地圖也是本文后續(xù)各節(jié)的索引簡單語法Promise 模板字符串類似zx的體驗(yàn)。腳本接口$命令提供更貼近 shell 的書寫方式。免轉(zhuǎn)義免引號無需 escaping 與 quoting從設(shè)計(jì)上杜絕 shell 注入風(fēng)險(xiǎn)詳見 docs/escaping.md。本地二進(jìn)制無需npx即可執(zhí)行項(xiàng)目本地安裝的 CLI 工具。增強(qiáng)的 Windows 支持正確處理 shebang、PATHEXT、優(yōu)雅終止等詳見 docs/windows.md。詳細(xì)錯誤、verbose 模式與自定義日志服務(wù)于調(diào)試詳見 docs/debugging.md。多子進(jìn)程管道可獲取中間結(jié)果、支持多源/多目標(biāo)與 unpipe詳見 docs/pipe.md。輸出切分/迭代按文本行切分或漸進(jìn)迭代。去除多余換行詳見 docs/lines.md。任意輸入類型文件、字符串、Uint8Array、迭代器、對象乃至幾乎任意其他類型分別參見 docs/input.md、docs/binary.md、docs/streams.md、docs/transform.md。任意輸出類型或?qū)⑤敵鲋囟ㄏ虻轿募?。交錯輸出stdout與stderr按終端真實(shí)打印順序交錯合并。編程式與終端輸出并存一邊在代碼中獲取結(jié)果一邊打印到控制臺。輸入輸出變換/過濾用簡單函數(shù)即可實(shí)現(xiàn)docs/transform.md。Node.js 流與 Web 流互操作或?qū)⒆舆M(jìn)程轉(zhuǎn)換為流docs/streams.md。子進(jìn)程消息通信docs/ipc.md。保證子進(jìn)程退出即使它攔截了終止信號或當(dāng)前進(jìn)程意外結(jié)束docs/termination.md。四、文檔地圖Execa 的官方文檔按主題拆分為獨(dú)立章節(jié)是深入學(xué)習(xí)每個特性的權(quán)威入口執(zhí)行類基本執(zhí)行、轉(zhuǎn)義/引號、Shell、腳本、Node.js 文件、環(huán)境、錯誤、終止。輸入輸出類輸入、輸出、文本行、二進(jìn)制數(shù)據(jù)、變換。高級用法多子進(jìn)程管道、流、進(jìn)程間通信、調(diào)試、Windows、與 Bash/zx 的差異、精簡包、TypeScript、API 參考。五、執(zhí)行示例5.1 簡單語法模板字符串execa可直接以標(biāo)簽?zāi)0遄址{(diào)用命令無需引號包裹參數(shù)插值也無需手動轉(zhuǎn)義import {execa} from execa; const {stdout} await execanpm run build; // 打印命令輸出 console.log(stdout);從源碼看這一語法糖由 lib/methods/template.js 中的parseTemplates()實(shí)現(xiàn)它會判斷傳入?yún)?shù)是否帶raw屬性的模板數(shù)組isTemplateString將其解析為[file, commandArguments, {}]形式再交給統(tǒng)一核心執(zhí)行。模板表達(dá)式${expression}支持字符串與數(shù)字也支持直接插入上一個子進(jìn)程的結(jié)果對象如${subprocess}的stdout若誤插入未await的 Promise 或ChildProcess會拋出TypeError提示請使用 ${await subprocess} 而不是 ${subprocess}。5.2 腳本接口$$接口與execa等價但預(yù)置了腳本友好的默認(rèn)選項(xiàng)。按 lib/methods/script.js 的實(shí)現(xiàn)當(dāng)未指定input、inputFile與stdio時$會自動設(shè)置stdin: inherit并且preferLocal: true作為深層次選項(xiàng)在管道場景中對兩個命令都生效import {$} from execa; const {stdout: name} await $cat package.json.pipegrep 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};注意最后一行目錄名含空格但模板插值讓 execa 將其作為單個參數(shù)傳遞無需任何引號或轉(zhuǎn)義。$還提供同步變體$.sync與別名$.s由setScriptSync掛載。5.3 本地二進(jìn)制無需 npx安裝項(xiàng)目本地依賴后用preferLocal: true選項(xiàng)即可直接執(zhí)行無需npx前綴$ npm install -D eslintawait execa({preferLocal: true})eslint;底層由npm-run-path庫見 lib/arguments/options.js 的getEnv()在preferLocal或node: true時自動拼接node_modules/.bin到 PATH 環(huán)境變量中從環(huán)境層面實(shí)現(xiàn)本地優(yōu)先解析。5.4 管道多個子進(jìn)程管道返回的 Promise 會解析為目標(biāo)子進(jìn)程的結(jié)果同時通過pipedFrom保留每一級中間結(jié)果const {stdout, pipedFrom} await execanpm run build .pipesort .pipehead -n 2; // 相當(dāng)于 npm run build | sort | head -n 2 的輸出 console.log(stdout); // 相當(dāng)于 npm run build | sort 的輸出 console.log(pipedFrom[0].stdout); // 相當(dāng)于 npm run build 的輸出 console.log(pipedFrom[0].pipedFrom[0].stdout);管道能力由 lib/pipe/setup.js 的pipeToSubprocess()驅(qū)動它把源子進(jìn)程的stdout/stderr/stdio接到目標(biāo)子進(jìn)程的stdin并Promise.race兩個子進(jìn)程的完成與 unpipe 中止信號.pipe()返回值還會轉(zhuǎn)發(fā)目標(biāo)的stdio、all、可迭代方法以及 IPC 方法sendMessage、getOneMessage、getEachMessage。對比 shell 管道execa 的優(yōu)勢是能拿到中間結(jié)果、支持一個源接多個目標(biāo)、多個源接一個目標(biāo)以及按需 unpipe詳見 docs/pipe.md。六、輸入輸出示例6.1 交錯輸出allall: true時返回結(jié)果的all字段包含stdout與stderr按實(shí)際打印順序交錯合并的內(nèi)容const {all} await execa({all: true})npm run build; // stdout stderr 交錯 console.log(all);該流由 lib/resolve/all-async.js 的makeAllStream()創(chuàng)建同時收集兩個通道并按時間順序合并。6.2 編程式輸出 終端輸出stdout選項(xiàng)設(shè)為[pipe, inherit]時輸出既被 execa 捕獲供程序讀取又同時打印到終端const {stdout} await execa({stdout: [pipe, inherit]})npm run build; // stdout 也會打印到終端 console.log(stdout);inherit意味著子進(jìn)程直接復(fù)用父進(jìn)程的標(biāo)準(zhǔn)流參見 docs/output.md 與 stdio 選項(xiàng)文檔 docs/api.md。6.3 簡單輸入const getInputString () { /* ... */ }; const {stdout} await execa({input: getInputString()})sort; console.log(stdout);字符串輸入會寫入子進(jìn)程的stdin并在結(jié)束后關(guān)閉詳見 docs/input.md。6.4 文件輸入 / 文件輸出// 類似: npm run build input.txt await execa({stdin: {file: input.txt}})npm run build; // 類似: npm run build output.txt await execa({stdout: {file: output.txt}})npm run build;stdin/stdout 支持{file: path}形式的文件描述對象由 lib/stdio/stdio-option.js 及 stdio 處理器展開為文件重定向。6.5 按文本行切分const {stdout} await execa({lines: true})npm run build; // 打印前 10 行 console.log(stdout.slice(0, 10).join(\n));lines: true時stdout從字符串變?yōu)樽址當(dāng)?shù)組并自動去除行尾換行符。從 lib/arguments/options.js 看lines生效還需滿足編碼為非二進(jìn)制且buffer開啟兩個前提條件。相關(guān)實(shí)現(xiàn)見 lib/io/strip-newline.js 與 lib/io/iterate.js。七、流式處理示例7.1 逐行迭代子進(jìn)程本身可直接被for await...of迭代逐行處理輸出for await (const line of execanpm run build) { if (line.includes(WARN)) { console.warn(line); } }迭代由 lib/convert/iterable.js 的createIterable()實(shí)現(xiàn)返回一個Symbol.asyncIterator按行產(chǎn)出文本相關(guān)測試見 test/io/iterate.js 與 test/stdio/iterable.js。7.2 轉(zhuǎn)換/過濾輸出stdout選項(xiàng)可接收一個生成器函數(shù)對每一行做變換或過濾let count 0; // 先過濾掉包含 secret 的行再為每行加上行號前綴 const transform function * (line) { if (!line.includes(secret)) { yield [${count}] ${line}; } }; await execa({stdout: transform})npm run build;變換管線的核心位于 lib/transform/run-async.js、lib/transform/run-sync.js 與 lib/transform/split.js生成器按文本行切分輸入逐個yield轉(zhuǎn)換后的行寫回目標(biāo)流。完整能力參見 docs/transform.md。7.3 Web 流直接傳入fetch返回的ReadableStream作為stdinconst response await fetch(https://example.com); await execa({stdin: response.body})sort;Web 流支持由 lib/convert/web.js 提供可把 Web 流、Node 流與子進(jìn)程的 stdio 統(tǒng)一起來。7.4 轉(zhuǎn)換為 Duplex 流子進(jìn)程可通過.duplex()轉(zhuǎn)換為雙工流與node:stream/promises的pipeline無縫銜接import {execa} from execa; import {pipeline} from node:stream/promises; import {createReadStream, createWriteStream} from node:fs; await pipeline( createReadStream(./input.txt), execanode ./transform.js.duplex(), createWriteStream(./output.txt), );duplex()由 lib/convert/duplex.js 實(shí)現(xiàn)本質(zhì)是把子進(jìn)程stdin作為可寫端、stdout作為可讀端封裝成PassThrough類雙工流倉庫還提供readable()、writable()、readableStream()、writableStream()、transformStream()等轉(zhuǎn)換方法見 lib/convert/add.js。八、進(jìn)程間通信IPC8.1 交換消息父進(jìn)程與子進(jìn)程可通過 promise 化的 API 雙向發(fā)消息子進(jìn)程側(cè)用execaNode啟動以保證 IPC 通道開啟// parent.js import {execaNode} from execa; const subprocess execaNodechild.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);IPC 實(shí)現(xiàn)分兩條鏈路父進(jìn)程側(cè)由 lib/ipc/methods.js 的addIpcMethods()把sendMessage/getOneMessage/getEachMessage掛到子進(jìn)程對象上子進(jìn)程側(cè)通過getIpcExport()導(dǎo)出同名函數(shù)。消息發(fā)送在 lib/ipc/send.js單條讀取在 lib/ipc/get-one.js逐條迭代在 lib/ipc/get-each.js。8.2 任意輸入類型ipcInputipcInput允許傳入包含正則、Set等復(fù)雜結(jié)構(gòu)的對象數(shù)組作為子進(jìn)程輸入且自動開啟ipc// 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();從 lib/arguments/options.js 的addDefaultOptions()可以看到默認(rèn)ipc ipcInput ! undefined || gracefulCancel即一旦傳入ipcInput或啟用優(yōu)雅取消IPC 通道自動打開消息序列化默認(rèn)采用serialization: advanced這也是能傳輸RegExp、Set等類型的原因lib/ipc/ipc-input.js 負(fù)責(zé)校驗(yàn)。8.3 任意輸出類型ipcOutput子進(jìn)程側(cè)多次sendMessage的結(jié)構(gòu)化消息會按序匯入父進(jìn)程結(jié)果的ipcOutput數(shù)組// main.js import {execaNode} from execa; const {ipcOutput} await execaNodebuild.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()});8.4 優(yōu)雅終止gracefulCancel通過AbortController與gracefulCancel: true組合可在取消時讓子進(jìn)程優(yōu)雅收尾——先通知子進(jìn)程自行清理而非立刻強(qiáng)殺// 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});取消相關(guān)的校驗(yàn)與執(zhí)行邏輯在 lib/terminate/cancel.js校驗(yàn)cancelSignal必須是AbortSignal并在中止時觸發(fā)kill()與 lib/terminate/graceful.js子進(jìn)程側(cè)通過getCancelSignal()拿到取消信號見 lib/ipc/graceful.js。完整終止語義參見 docs/termination.md。九、調(diào)試與日志9.1 詳細(xì)錯誤對象命令失敗時拋出的ExecaError同步版為ExecaSyncError定義于 lib/return/final-error.js攜帶極其豐富的診斷字段import {execa, ExecaError} from execa; try { await execaunknown 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 ] } } */ }錯誤對象在 lib/return/result.js 的makeError()中組裝包含command/escapedCommand、退出碼code、stdout/stderr/stdio快照、是否超時/取消/超緩沖等標(biāo)志原始 spawn 錯誤則作為error.cause保留。完整錯誤語義見 docs/errors.md。9.2 Verbose 模式不附加任何配置連續(xù)運(yùn)行多個命令時execa 會自動輸出分步、分色的執(zhí)行日志命令、時長、退出碼等await execanpm run build; await execanpm run test;運(yùn)行效果示意如下倉庫 media/verbose.pngverbose 輸出的生成與格式控制分布在 lib/verbose/ 目錄start.js、complete.js、output.js、error.js、ipc.js等相關(guān)測試見 test/verbose/。9.3 自定義日志verbose選項(xiàng)可傳入回調(diào)函數(shù)將 execa 的事件流接入任意日志框架示例使用 Winstonimport {execa as execa_} from execa; import {createLogger, transports} from winston; // 用 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]; loggerlevel; }, }); await execanpm run build; await execanpm run test;注意這里通過execa_(options)預(yù)先綁定選項(xiàng)生成新的execa實(shí)例——這正是 lib/methods/create.js 中options binding能力的體現(xiàn)當(dāng)createExeca生成的函數(shù)收到一個純對象作為首個參數(shù)時它會合并選項(xiàng)并返回一個綁定了這些默認(rèn)選項(xiàng)的嵌套版本。verbose回調(diào)的type字段涵蓋command、output、ipc、error、duration等事件類型可據(jù)此映射日志級別參見 lib/verbose/custom.js。十、與其他方案的區(qū)別Execa 被定位為編程化進(jìn)程執(zhí)行工具與交互式 shell 腳本有本質(zhì)區(qū)別核心差異包括詳見 docs/bash.md基于 Promise所有命令都可await、可并行、可組合天然融入異步代碼流。模板字符串代替拼接參數(shù)以數(shù)組形式傳遞空格、特殊字符無需引號消除注入面。結(jié)構(gòu)化結(jié)果與錯誤返回包含stdout/stderr/exitCode/durationMs的對象錯誤也是可編程檢查的結(jié)構(gòu)。本地二進(jìn)制優(yōu)先自動解析node_modules/.bin省去npx??缙脚_一致對 Windows 的 shebang、PATHEXT做了兼容處理docs/windows.md。類型安全完整的 TypeScript 類型定義見 types/ 與 test-d/類型級測試保證編譯期發(fā)現(xiàn)錯誤用法。十一、小結(jié)Execa 用統(tǒng)一的 Promise API 覆蓋了進(jìn)程執(zhí)行的全場景從最基礎(chǔ)的execa/execaSync/execaNode/$四種入口導(dǎo)出定義見 index.js到模板字符串解析lib/methods/template.js、選項(xiàng)歸一化與默認(rèn)值lib/arguments/options.js、異步核心執(zhí)行l(wèi)ib/methods/main-async.js、流式轉(zhuǎn)換lib/transform/、管道編排lib/pipe/、IPC 消息lib/ipc/與終止控制lib/terminate/再配合詳細(xì)的錯誤對象與可定制日志讓你以接近 shell 的簡潔、遠(yuǎn)超 shell 的可靠性完成進(jìn)程編排。深入每個主題時官方文檔見上文文檔地圖與倉庫內(nèi) test/ 目錄下的對應(yīng)測試用例都是最佳參考資料。贊分享開發(fā)工具【免費(fèi)下載鏈接】execaProcess execution for humans項(xiàng)目地址https://gitcode.com/gh_mirrors/ex/execa點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Execa 基礎(chǔ)執(zhí)行完全指南數(shù)組語法、模板字符串語法與返回值詳解Execa 基礎(chǔ)執(zhí)行完全指南數(shù)組語法、模板字符串語法與返回值詳解 ExecaProcess execution for humans是構(gòu)建在 Node.j開發(fā)工具基于 PHP 可變函數(shù)與字符串轉(zhuǎn)義的遠(yuǎn)程命令執(zhí)行與 WAF 繞過實(shí)戰(zhàn)指南webshell 倉庫配套基于 PHP 可變函數(shù)與字符串轉(zhuǎn)義的遠(yuǎn)程命令執(zhí)行與 WAF 繞過實(shí)戰(zhàn)指南webshell 倉庫配套 本篇技術(shù)指南以倉庫文檔 How To Exploit P網(wǎng)絡(luò)安全滲透測試TypeScript 模板字符串Template Literals實(shí)戰(zhàn)指南插值、多行與標(biāo)簽?zāi)0錞ypeScript 模板字符串Template Literals實(shí)戰(zhàn)指南插值、多行與標(biāo)簽?zāi)0?導(dǎo)讀 模板字符串Template Literals又稱教程上一篇MediaMTX 定時抓幀快照基于 runOnAvailable 鉤子 FFmpeg 的流截圖方案下一篇【親測免費(fèi)】 VRM-Addon-for-Blender 項(xiàng)目推薦創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考