Execa 7을 사용한 셸 없는 스크립트

Mar 12 2023
셸 스크립트는 매우 편리하지만 많은 개발자가 JavaScript와 같은 프로그래밍 언어와 함께 제공되는 유연성, 표현력 및 생태계를 즐깁니다. Google의 최근 인기 프로젝트인 zx는 두 세계의 장점을 결합합니다.
조개가 거의 없는 해변(Aaron Burden의 사진)

셸 스크립트는 매우 편리하지만 많은 개발자가 JavaScript와 같은 프로그래밍 언어와 함께 제공되는 유연성, 표현력 및 생태계를 즐깁니다. zxGoogle의 최근 인기 프로젝트인 은 두 세계의 장점을 결합합니다.

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

Node와 Bash를 모두 사용하는 이유는 무엇입니까? Execa를 사용하면 기억해야 할 셸 구문이 없습니다. 모든 것이 평범한 JavaScript입니다. 거의 모든 쉘 관련 기능을 JavaScript로 표현할 수 있습니다. 나머지 엣지 케이스의 경우 shell옵션을 사용할 수 있습니다.

이것은 더 많은 것입니다:

  • 보안 : 쉘 주입은 기술적으로 실현 가능하지 않습니다.
  • 교차 플랫폼 : 모든 Windows 시스템에서 Bash를 사용할 수 있는 것은 아닙니다.
  • 수행자 : 모든 명령에 대해 쉘을 생성하는 데 비용이 듭니다. Execa는 로컬에 설치된 바이너리를 직접 실행할npx 수 있으므로 건너뛸 수도 있습니다 .
  • # 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 },
)

핵심 child_process노드 모듈에는 시간 초과, 취소, 백그라운드 프로세스 , IPC , PID , UID/GID, 약한 참조 등 많은 유용한 기능이 포함되어 있습니다. Execa는 우아한 종료 , 정리 , 인터리브 출력 등 몇 가지 추가 항목을 추가합니다 . 이러한 항목은 하나 또는 여러 명령을 사용하여 설정할 수 있습니다 .$(options)

import { $ } from 'execa'

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

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

하위 프로세스는 디버그하기 어려울 수 있습니다. 이것이 바로 Execa가 다음과 같은 이유입니다.

  • 를 사용하여 설정할 수 있는 verbose옵션이 포함되어 있습니다 NODE_DEBUG=execa.
  • 자세한 오류 메시지 및 속성을 보고합니다 .
  • 순전히 상태 비저장이므로 현재 디렉토리 또는 기타 옵션이 무엇인지 쉽게 파악할 수 있습니다.
  • > 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)