Shell-freie Skripte mit Execa 7

Mar 12 2023
Obwohl Shell-Skripte sehr praktisch sind, genießen viele Entwickler die Flexibilität, Ausdruckskraft und das Ökosystem, das Programmiersprachen wie JavaScript mit sich bringen. zx, ein aktuelles beliebtes Projekt von Google, vereint das Beste aus beiden Welten.
Fast muschelfreier Strand (Foto von Aaron Burden)

Obwohl Shell-Skripte sehr praktisch sind, genießen viele Entwickler die Flexibilität, Ausdruckskraft und das Ökosystem, das Programmiersprachen wie JavaScript mit sich bringen. zx, ein aktuelles beliebtes Projekt von Google, vereint das Beste aus beiden Welten.

const branch = await $`git branch --show-current`
await $`deploy --branch=${branch}`

import { $ } from 'execa'

const branch = await $`git branch --show-current`
await $`deploy --branch=${branch}`

Warum sowohl Node als auch Bash verwenden? Bei Execa muss man sich keine Shell-Syntax merken: Alles ist einfach nur JavaScript. Fast alle Shell-spezifischen Funktionen können in JavaScript ausgedrückt werden. Für die übrigen Randfälle steht eine shellOption zur Verfügung.

Das ist mehr:

  • Sicher : Shell-Injektionen sind technisch nicht machbar.
  • Plattformübergreifend : Nicht auf allen Windows-Computern ist Bash verfügbar.
  • Performant : Das Erzeugen einer Shell für jeden Befehl ist mit Kosten verbunden. Kann sogar npxübersprungen werden, da Execa lokal installierte Binärdateien direkt ausführen kann.
  • # In Bash
    API_KEY="secret" npx deploy "$cluster" &> logs.txt && echo "done"
    // In JavaScript
    import { $ } from 'execa'
    
    const options = { env: { API_KEY: 'secret' } }
    await $(options)`deploy ${cluster}`.pipeAll(’logs.txt’)
    console.log('done')
    

import { $ } from 'execa'
import pRetry from 'p-retry'

await pRetry(
  async () => await $`deploy dev_cluster`,
  { retries: 5 },
)

Das child_processKernknotenmodul enthält viele nützliche Funktionen: Timeout, Abbruch, Hintergrundprozesse , IPC , PID , UID/GID, schwache Referenzen und mehr. Execa fügt ein paar zusätzliche hinzu : Graceful Termination , Cleanup , Interleaved Output$(options) usw. Diese können für einen oder mehrere Befehle festgelegt werden .

import { $ } from 'execa'

const $$ = $({ timeout: 5000, all: true })

// `all` retrieves both stdout and stderr
const { all } = $$`deploy dev_cluster`

Untergeordnete Prozesse können schwer zu debuggen sein. Aus diesem Grund: Execa:

  • Enthält eine verboseOption , die mit eingestellt werden kann NODE_DEBUG=execa.
  • Meldet detaillierte Fehlermeldungen und Eigenschaften.
  • Ist rein zustandslos, wodurch es einfach ist, das aktuelle Verzeichnis oder eine andere Option herauszufinden.
  • > NODE_DEBUG=execa node deploy.js
    
    [16:50:03.305] deploy dev_cluster
    ........dev_cluster successfully deployed.
    
    [16:53:06.378] deploy prod_cluster
    .............prod_cluster successfully deployed.
    

import { $ } from 'execa'

// Pipe input from a string or buffer, like <<< in Bash
await $({ input: 'dev_cluster' })`deploy`

// Pipe input from a file, like < in Bash
await $({ inputFile: 'clusters.txt' })`deploy`

// Pipe output to a file, like >, 2> and &> in Bash
await $`deploy dev_cluster`.pipeStdout('stdout.txt')
await $`deploy dev_cluster`.pipeStderr('stderr.txt')
await $({ all: true })`deploy dev_cluster`.pipeAll('logs.txt')

// Pipe output to another command, like | and |& in Bash
await $`deploy dev_cluster`.pipeStdout($`grep done`)

// Pipe output to a stream, like 2>&1 in Bash
const { stdout } = await $`deploy dev_cluster`.pipeStderr(process.stdout)