Google zx scaffold module Chinese document

Posted by russthebarber on Thu, 10 Mar 2022 11:29:35 +0100

Google zx scaffold module Chinese document

zx is a new star project on the 2021 gibhub. It allows us to easily use JavaScript / TypeScript (which contains TypeScript type declaration) instead of bash to build command-line scripts. The script provides convenience for those who master the front-end development to build a scaffold. Recently, I paid attention to the project and translated its enabling documents for sharing as notes.

zx official Github homepage:

zx official npm homepage:


translator: jcLee95


🐚 zx



Markdown script


🐚 zx

#!/usr/bin/env zx

await $`cat package.json | grep name`

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

await Promise.all([
  $`sleep 1; echo 1`,
  $`sleep 2; echo 2`,
  $`sleep 3; echo 3`,

let name = 'foo bar'
await $`mkdir /tmp/${name}`

Bash is great, but when writing scripts, people usually choose a more convenient programming language.
JavaScript is a perfect choice, but standard node JS library needs extra trouble before using. zx surrounds child_process provides a useful wrapper to escape parameters and give reasonable default values.


npm i -g zx

Requirement: Node version > = 16.0.0


When the extension is Script in mjs file so that await can be used at the top level. If you like it js extension, which can wrap your script like void async function() {...} ().
Add the following statement to the beginning of your zx script:

#!/usr/bin/env zx

You will now be able to run your script like this:

chmod +x ./script.mjs

Or through zx executable:

zx ./script.mjs

All functions ($, cd, fetch, etc.) can be used directly without any import.

Or explicitly import global variables (for better automation in VS Code).

import 'zx/globals'


Using child_ The spawn function in the process package executes the given string and returns processpromise < processoutput >.

Everything goes through ${...} Will be automatically escaped and referenced.

let name = 'foo & bar'
await $`mkdir ${name}`

No additional quotation marks are required. stay quotes Read more about it in

If desired, you can pass a set of parameters:

let flags = [
await $`git log ${flags}`

If the executed program returns a non-zero exit code, ProcessOutput is thrown.

try {
  await $`exit 1`
} catch (p) {
  console.log(`Exit code: ${p.exitCode}`)
  console.log(`Error: ${p.stderr}`)


class ProcessPromise<T> extends Promise<T> {
  readonly stdin: Writable
  readonly stdout: Readable
  readonly stderr: Readable
  readonly exitCode: Promise<number>
  pipe(dest): ProcessPromise<T>
  kill(signal = 'SIGTERM'): Promise<void>

The pipe() method can be used to redirect stdout:

await $`cat file.txt`.pipe(process.stdout)

Read more about pipelines Information about.


class ProcessOutput {
  readonly stdout: string
  readonly stderr: string
  readonly exitCode: number
  readonly signal: 'SIGTERM' | 'SIGKILL' | ...
  toString(): string



Change the current working directory.

await $`pwd` // outputs /tmp


node-fetch Packaging of bags.

let resp = await fetch('')
if (resp.ok) {
  console.log(await resp.text())


readline A wrapper for a bag.


let bear = await question('What kind of bear is best? ')
let token = await question('Choose env variable: ', {
  choices: Object.keys(process.env)

In the second parameter, you can refer to the array of options for automatic completion of custom table symbols.

function question(query?: string, options?: QuestionOptions): Promise<string>
type QuestionOptions = { choices: string[] }


Wrapper for the setTimeout function.

await sleep(1000)


Change the behavior of $to not throw exceptions on non-zero exit codes.

function nothrow<P>(p: P): P


await nothrow($`grep something from-file`)

// Inside a pipe():

await $`find ./examples -type f -print0`
  .pipe(nothrow($`xargs -0 grep something`))
  .pipe($`wc -l`)

If you only need exitCode, you can use the next code:

if (await $`[[ -d path ]]`.exitCode == 0) {

// amount to:

if ((await nothrow($`[[ -d path ]]`)).exitCode == 0) {


Change the behavior of $to disable verbose output.

function quiet<P>(p: P): P


await quiet($`grep something from-file`)
// Commands and output are not displayed.


The following packages can be used without importing internal scripts.

chalk package

chalk Bag.

console.log('Hello world!'))

yaml package

yaml Bag.

console.log(YAML.parse('foo: bar').foo)

fs package

fs-extra Bag.

let content = await fs.readFile('./package.json')

globby package

globby package

let packages = await globby(['package.json', 'packages/*/package.json'])

let pictures = globby.globbySync('content/*.(jpg|png)')

In addition, globby can be obtained through glob shortcut:

await $`svgo ${await glob('*.svg')}`

os package

os package

await $`cd ${os.homedir()} && mkdir example`

path package

path Bag.

await $`mkdir ${path.join(basedir, 'output')}`

minimist package

minimist Bag.

Supplied as a global constant argv.

to configure


Specify what shell to use. The default is which bash.

$.shell = '/usr/bin/bash'

Or use a CLI parameter: -- shell=/bin/bash


Specifies the command that will prefix all running commands.

The default is set -euo pipefail

Or use a CLI parameter: -- prefix='set -e; '


Specifies a function to escape special characters during command replacement.


Specify the detail level.
The default value is true

In verbose mode, zx prints all executed commands and their output.

Or use a CLI parameter: -- quiet to set $ verbose = false.


__filename & __dirname

stay ESM In the module, node JS does not provide__ filename and__ dirname global variable. Because such global variables are very convenient in scripts, zx provides these for mjs file (when using zx executable).


stay ESM In the module, the require() function is not defined.
zx provides the require() function, so it can be used with Used with imports in mjs files (when using zx executables).

let {version} = require('./package.json')


zx also provides some experimental functions. Please in discuss Leave feedback on these features in.


Retry the command several times. Will return after the first successful attempt, or will be raised after the specified number of attempts.

import {retry} from 'zx/experimental'

let {stdout} = await retry(5)`curl localhost`


Acceptable ProcessOutput Console Log() alternative.

import {echo} from 'zx/experimental'

let branch = await $`git branch --show-current`

echo`Current branch is ${branch}.`
// perhaps
echo('Current branch is', branch)


Start a simple CLI spinner and return the stop() function.

import {startSpinner} from 'zx/experimental'

let stop = startSpinner()
await $`long-running command`


Passing environment variables

process.env.FOO = 'bar'
await $`echo $FOO`

Pass value array

If an array of values is passed as a parameter to $, the items of the array will be escaped separately and connected by spaces.

For example:

let files = [...]
await $`tar cz ${files}`

Import from other scripts

You can use $and other functions through explicit import:

#!/usr/bin/env node
import {$} from 'zx'
await $`date`

Script without extension

If the script does not have a file extension (for example,. Git / hooks / pre commit), zx assumes it is a ESM modular.

Markdown script

zx can execute with markdown Script written by.

zx docs/

TypeScript script

import {$} from 'zx'
// Or 
import 'zx/globals'

void async function () {
  await $`ls -la`

use ts-node As an esm node loader.

node --loader ts-node/esm script.ts

You must i in package Set in JSON "type": "module" And in tsconfig Set in JSON "module": "ESNext" .

  "type": "module"
  "compilerOptions": {
    "module": "ESNext"

Execute remote script

If the parameter of zx executable starts with https: / /, the file will be downloaded and executed.


Execute script from stdin

zx supports script execution from stdin.

zx <<'EOF'
await $`pwd`



Disclaimer: This is not an officially supported Google product.

Quotes (quotation marks)

Pass parameters to ${...} No quotation marks are required. If necessary, it will be added automatically.

let name = 'foo & bar'
await $`mkdir ${name}`

For quotation marks, zx uses a special bash syntax (the next command is valid bash):

mkdir $'foo & bar'
$'ls' $'-la'

If you add quotation marks "${name}", an incorrect command will be generated.

If you need to add something extra, consider putting it in curly braces.

await $`mkdir ${'path/to-dir/' + name}`

This will also work:

await $`mkdir path/to-dir/${name}`

Parameter array

zx can also be in ${...} Accept arrays or parameters in. The items in the array will be referenced separately and connected by spaces.

Do not add join(' ').

let flags = [
await $`git log ${flags}`

If you already have a string with an array, it's your responsibility to parse it correctly and distinguish different parameters. Like this:

await $`git log ${'--oneline --decorate --color'.split(' ')}`

globbing and~

When everything passes ${...} Will be escaped, cannot use ~ or glob syntax.

To achieve this, zx provides globby package.

Not like this:

let files = '~/dev/**/*.md' // wrong
await $`ls ${files}`

Use glob function and os package:

let files = await glob(os.homedir() + '/dev/**/*.md')
await $`ls ${files}`

zx supports node JS stream, a special pipe() method, can be used to redirect standard output.

await $`echo "Hello, stdout!"`

await $`cat /tmp/output.txt`

Process created with $from process Stdin gets stdin, but we can also write to child processes:

let p = $`read var; echo "$var";`
p.stdin.write('Hello, stdin!\n')

let {stdout} = await p

The pipeline can be used to display the real-time output of the program:

$.verbose = false

await $`echo 1; sleep 1; echo 2; sleep 1; echo 3;`

Pipe stdout and stderr:

let echo = $`echo stdout; echo stderr 1>&2`
await echo

In addition, the pipe() method can combine $programs. Same as | in bash:

let greeting = await $`printf "hello"`
  .pipe($`awk '{printf $1", world!"}'`)
  .pipe($`tr '[a-z]' '[A-Z]'`)


Using pipe() and nothrow():

await $`find ./examples -type f -print0`
  .pipe(nothrow($`xargs -0 grep ${'missing' + 'part'}`))
  .pipe($`wc -l`)
Markdown script

It is possible to write scripts using markdown. zx executes only blocks of code.

You can run this markdown file:

zx docs/
await $`whoami`
await $`echo ${__dirname}`

__ filename will point to markdown md:


We can also use import here:

await import('chalk')

Bash code (with bash or sh language tags) will also be executed:

echo "$VAR" | wc -c

Other code blocks ignored:

body .hero {
    margin: 42px;

Topics: Javascript TypeScript