zx

Comprehensive guide for writing shell scripts with Google zx — a tool for writing better scripts using JavaScript/TypeScript. Use when writing, debugging, or refactoring zx scripts (.mjs, .js, .ts files using zx), executing shell commands from JavaScript, working with ProcessPromise/ProcessOutput APIs, piping streams, configuring zx options, or using zx CLI. Do NOT use for general Node.js questions unrelated to shell scripting.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "zx" with this command: npx skills add openlark/zx

Zx — Write Better Shell Scripts with JavaScript

Overview

zx is Google's tool for writing shell scripts in JavaScript/TypeScript. It wraps child_process, auto-escapes arguments, and provides sensible defaults — giving you the power of the JavaScript ecosystem in your scripts.

#!/usr/bin/env zx

await $`cat package.json | grep name`

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

const name = 'foo & bar'
await $`mkdir /tmp/${name}`  // No quotes needed — auto-escaped

Bash is great for simple tasks, but when scripts grow complex, a full programming language helps. zx adds helpful wrappers around child_process, escapes arguments, and gives sensible defaults. Think: bash + JavaScript in one script.

Triggers

Also triggers when users ask about running shell commands in JavaScript, converting bash scripts to zx, executing remote scripts, Markdown scripts, or TypeScript shell scripts.

Quick Start

npm install zx

Write scripts as .mjs files (supports top-level await). Add #!/usr/bin/env zx shebang or run via CLI:

zx ./script.mjs           # Direct execution
npx zx ./script.mjs       # Via npx
node --import zx/globals  # As Node.js loader

All functions ($, cd, fetch, etc.) are globally available in zx scripts without imports. For explicit imports (better VS Code autocomplete):

import 'zx/globals'

Core Concepts

$`command` — Execute Shell Commands

The tagged template literal is the heart of zx. Everything in ${...} is auto-escaped and quoted.

// Async (standard) — returns ProcessPromise
const output = await $`ls -la`

// Sync variant — returns ProcessOutput directly
const dir = $.sync`pwd`

// Arrays are flattened
const flags = ['--oneline', '--decorate', '--color']
await $`git log ${flags}`

// Non-zero exit codes throw ProcessOutput
try {
  await $`exit 1`
} catch (p) {
  console.log(`Exit: ${p.exitCode}, Error: ${p.stderr}`)
}

Preset Configuration with $({...})

Create custom $ instances with preset options — chainable and composable:

const $$ = $({ verbose: false, env: { NODE_ENV: 'production' } })
const pwd = $$.sync`pwd`

// Presets are chainable
const $1 = $({ nothrow: true })
const $2 = $1({ sync: true })  // Both nothrow + sync applied

ProcessPromise & ProcessOutput

$`cmd`              ProcessPromise (extends Promise)
  ├── .pipe()       Stream piping
  ├── .kill()       Terminate process
  ├── .text()       Output as string
  ├── .json()       Output as parsed JSON
  ├── .lines()      Output split by lines
  ├── .nothrow()    Suppress errors for this command
  ├── .quiet()      Suppress output for this command
  ├── .timeout()    Auto-kill after duration
  ├── .stdio()      Configure I/O
  ├── .exitCode     Promise<exit code>
  ├── .stdout       Readable stream
  ├── .stderr       Readable stream
  ├── .stdin        Writable stream
  ├── .pid / .cmd   Process metadata
  └── await → ProcessOutput
                ├── .stdout    string
                ├── .stderr    string
                ├── .exitCode  number
                ├── .signal    string|null
                ├── .text() / .json() / .lines() / .buffer() / .blob()
                └── .ok        boolean (when nothrow)

Decision Tree

When writing zx scripts, use this decision tree:

GoalApproach
Run a commandawait $`cmd`
Run synchronously$.sync`cmd`
Pipe output.pipe($`next`) / .pipe('file.txt')
Handle errors gracefully$({nothrow: true}) / .nothrow()
Set timeout$({timeout: '30s'}) / .timeout('30s')
Parse JSON output(await $`cmd`).json()
Real-time streamingfor await (const line of $`cmd`)
Retry on failureretry(5, () => $`cmd`)
User promptquestion('Name: ')
Progress indicatorawait spinner('Working...', () => $`cmd`)
Change directorycd('/path') or within(() => { $.cwd = '/tmp' })
Temp files/dirstmpfile() / tmpdir()
Parse CLI argsargv.flag or minimist(process.argv.slice(2))
Load .env filedotenv.config('.env')

Writing Effective zx Scripts

Parallel Execution

const results = await Promise.all([
  $`sleep 1; echo 1`,
  $`sleep 2; echo 2`,
  $`sleep 3; echo 3`,
])

Error Handling with nothrow

$.nothrow = true

const repos = ['zx', 'webpod']
const clones = repos.map(n => $`git clone https://github.com/google/${n}`)

const results = await Promise.all(clones)
const errors = results.filter(o => !o.ok).map(o => o.stderr.trim())
console.log('Errors:', errors.join('\n'))

Stream Piping

// Chain commands like bash pipes
const greeting = await $`printf "hello"`
  .pipe($`awk '{printf $1", world!"}'`)
  .pipe($`tr '[a-z]' '[A-Z]'`)

// Pipe to file
await $`echo "Hello!"`.pipe('/tmp/output.txt')

// Real-time output to terminal
await $`echo 1; sleep 1; echo 2; sleep 1; echo 3`.pipe(process.stdout)

Stream Splitting & Merging

// Split one source to multiple consumers
const p = $`some-command`
const [o1, o2] = await Promise.all([
  p.pipe`log`,
  p.pipe`extract`,
])

// Merge multiple sources
const $h = $({ halt: true })
const p1 = $`echo foo`
const p2 = $h`echo a && sleep 0.1 && echo b`
const p3 = $h`echo c && sleep 0.1 && echo d`
const cat = $h`cat`
p1.pipe(cat); p2.pipe(cat); p3.pipe(cat)
await cat.run()

Output Formatters

const p = $`echo '{"foo":"bar"}\nline2'`

await p.json()    // { foo: 'bar' }
await p.lines()   // ['{"foo":"bar"}', 'line2']
await p.text()    // '{"foo":"bar"}\nline2\n'

Async Iteration

for await (const line of $`git log --oneline --max-count=5`) {
  console.log(line)
}

Shell Configuration

zx defaults to bash. Switch shells as needed:

import { useBash, usePowerShell, usePwsh } from 'zx'

usePowerShell()  // PowerShell.exe
usePwsh()        // PowerShell v7+
useBash()        // Back to bash

// Manual override
$.shell = '/bin/zsh'

On Windows, consider using WSL or Git Bash for bash support, or switch to PowerShell via usePowerShell().

Built-in Helpers Summary

HelperPurposeExample
cd()Change directorycd('/tmp')
fetch()HTTP requests, supports .pipe()fetch('https://api.example.com')
question()Interactive user inputquestion('Name: ')
sleep()Delay executionawait sleep(1000)
echo()Print to stdoutecho`Status: ${p}`
stdin()Read stdinJSON.parse(await stdin())
within()Isolated async config contextwithin(() => { $.cwd = '/tmp' })
retry()Retry with delay/backoffretry(5, () => $curl url)
spinner()CLI progress indicatorawait spinner(() => $long-cmd)
glob()File glob matching (globby)glob('**/*.js')
which()Find executable pathawait which('node')
psCross-platform process listps.lookup({ command: 'node' })
tmpdir()Temp directorytmpdir('sub')
tmpfile()Temp filetmpfile('f.txt', 'content')
argvParsed CLI argumentsargv.verbose
dotenv.env file loadingdotenv.config('.env')

Exposed npm packages: chalk (colors), fs (fs-extra), os, path, YAML (yaml), minimist.

Resources

  • references/api.md — Full API reference: $ options, cd(), fetch(), question(), sleep(), echo(), stdin(), within(), retry(), spinner(), glob(), which(), ps, kill(), tmpdir(), tmpfile(), minimist, argv, chalk, fs, os, path, YAML, dotenv, quote(), useBash(), usePowerShell(), usePwsh()
  • references/configuration.md — All $.options: shell, prefix, postfix, quote, verbose, quiet, env, cwd, timeout, nothrow, detached, preferLocal, spawn, kill, log, input, signal, stdio, halt, delimiter, defaults
  • references/cli.md — CLI usage: flags, env vars, Markdown scripts, remote scripts, stdin execution, REPL mode
  • references/process.md — ProcessPromise/ProcessOutput lifecycle, piping, killing, aborting, output formatters, stream handling

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

China Top Doctor Referral

OceanBus-powered top-tier hospital specialist referral service for high-end medical insurance clients. Use when users need to search 1,600+ leading specialis...

Registry SourceRecently Updated
Coding

Nm Gauntlet Extract

Analyze a codebase and build a knowledge base of business logic, architecture, data flow, and engineering patterns. The foundation for gauntlet challenges an...

Registry SourceRecently Updated
1790athola
Coding

Desktop Gui

全桌面 GUI 自动化 — 使用 Python 库 (pyautogui, opencv) + xdotool + scrot,支持鼠标/键盘模拟、截图、视觉识别。

Registry SourceRecently Updated
Coding

Nm Sanctum Doc Updates

Update documentation after code changes with quality gates, slop detection, consolidation, and accuracy verification

Registry SourceRecently Updated
1590athola