Script senza shell con Execa 7

Mar 12 2023
Sebbene gli script di shell siano molto utili, molti sviluppatori apprezzano la flessibilità, l'espressività e l'ecosistema forniti da linguaggi di programmazione come JavaScript. zx, un recente progetto popolare di Google, combina il meglio dei due mondi.
Spiaggia quasi priva di conchiglie (foto di Aaron Burden)

Sebbene gli script di shell siano molto utili, molti sviluppatori apprezzano la flessibilità, l'espressività e l'ecosistema forniti da linguaggi di programmazione come JavaScript. zx, un recente progetto popolare di Google, combina il meglio di entrambi i mondi.

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}`

Perché usare sia Node che Bash? Con Execa, non c'è una sintassi della shell da ricordare: tutto è semplicemente JavaScript. Quasi tutte le funzionalità specifiche della shell possono essere espresse in JavaScript. Per i rimanenti casi limite, è disponibile shellun'opzione .

Questo è più:

  • Sicuro : le iniezioni di shell non sono tecnicamente fattibili.
  • Multipiattaforma : non tutte le macchine Windows hanno Bash disponibile.
  • Performante : generare una shell per ogni comando ha un costo. Anche npxpuò essere saltato poiché Execa può eseguire direttamente i binari installati localmente.
  • # 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 },
)

Il child_processmodulo Node principale include molte funzioni utili: timeout, cancellazione, processi in background , IPC , PID , UID/GID, riferimenti deboli e altro. Execa ne aggiunge alcuni aggiuntivi : terminazione graziosa , pulizia , output interleaved , ecc. Questi possono essere impostati utilizzando $(options)uno o più comandi.

import { $ } from 'execa'

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

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

I processi figlio possono essere difficili da eseguire il debug. Per questo Execa:

  • Include un'opzione verbose, che può essere impostata utilizzando NODE_DEBUG=execa.
  • Segnala messaggi di errore dettagliati e proprietà.
  • È puramente senza stato, rendendo semplice capire qual è la directory corrente o qualsiasi altra opzione.
  • > 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)