Google ZX Scripting
Use this skill when writing scripts with Google's zx library — the tool that makes shell scripting with JavaScript/TypeScript productive and safe. Read only the reference file(s) needed for the task.
Quick Start
All zx scripts use the lite entry point for portability. Scripts are standalone .mjs files — no project setup required.
#!/usr/bin/env npx zx
import { $, fs, path, glob, chalk } from 'zx/core';
const files = await glob('src/**/*.ts');
await $`eslint ${files}`;
Run directly:
chmod +x script.mjs && ./script.mjs
# or
npx zx script.mjs
Critical Rules
- Always use
zx/core— never barezx. Avoids the heavy CLI wrapper. - Template literals auto-escape —
$`echo ${userInput}`is safe. Never manually quote interpolated variables. - Arrays expand correctly —
$`cmd ${arrayOfArgs}`expands each element as a separate quoted argument. - Non-zero exits throw — use
.nothrow()to suppress when you expect failures (e.g.,grepwith no matches). within()for isolation — creates an async scope with its own$.cwdand$.env. Essential for parallel tasks.- Pipe with
.pipe()— use$`cmd1`.pipe($`cmd2`)instead of shell|. - Prefer zx builtins —
glob()overfind,fsovercat/cp/mv,fetch()overcurl. cd()is global — usewithin()or$({cwd: '/path'})for scoped directory changes.- Scripts must be
.mjs— for top-levelawaitsupport without bundler config.
Reference Map
| Need | File |
|------|------|
| $, ProcessPromise, ProcessOutput, configuration, piping, streams | references/core-api.md |
| Ad-hoc scripts, CLI tools, build scripts, deployment, project scaffolding | references/scripting-patterns.md |
| File processing, data pipelines, batch ops, AI scripts, log analysis | references/processing-recipes.md |
Task Routing
- Writing a quick one-off command or shell automation ->
references/scripting-patterns.md - Building a build/deploy/CI script ->
references/scripting-patterns.md - Processing files, data, logs, or batch operations ->
references/processing-recipes.md - Need API details for
$, pipes, streams, config ->references/core-api.md - Combining multiple patterns (e.g., build + process) -> read both relevant files
Import Cheatsheet
// Core — always start here
import { $, fs, path, glob, chalk } from 'zx/core';
// Additional utilities (import individually as needed)
import { spinner, retry, question, echo, sleep, within,
stdin, tmpdir, tmpfile, which, ps, kill,
quote, YAML, argv, fetch } from 'zx/core';
Validation Checkpoints
Before executing destructive or bulk operations, always verify scope:
// Verify glob matches before bulk processing
const files = await glob('src/**/*.ts');
console.log(`Found ${files.length} files`);
if (files.length === 0) throw new Error('No files matched — check glob pattern');
if (files.length > 500) throw new Error(`Too many files (${files.length}) — narrow the glob`);
// Dry-run flag for destructive operations
const dryRun = argv['dry-run'] ?? false;
for (const file of files) {
if (dryRun) { console.log(`[dry-run] would delete ${file}`); continue; }
await fs.rm(file);
}
For parallel operations, use Promise.allSettled() to handle partial failures:
const results = await Promise.allSettled(
files.map(f => within(async () => $`process ${f}`))
);
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) {
console.error(chalk.red(`${failed.length}/${results.length} tasks failed`));
failed.forEach(r => console.error(r.reason));
process.exitCode = 1;
}
Conventions for Generated Scripts
- Shebang line: Always include
#!/usr/bin/env npx zxas the first line - Verbose off: Set
$.verbose = falsebefore main logic to suppress command echoing - Error handling: Use the
main().catch()pattern — wrap logic inasync function main(), then callmain().catch(err => { console.error(chalk.red(err.message)); process.exit(1); }) - Output colors: Use
chalk.green()for success,chalk.red()for errors,chalk.yellow()for warnings,chalk.dim()for secondary info - Progress: Use
spinner()for long-running operations in interactive contexts - Arguments: Use
argv(pre-parsedminimist) for CLI argument handling - Temp files: Use
tmpdir()andtmpfile()— they auto-clean on exit - Parallelism: Use
Promise.all()withwithin()for parallel operations that need isolation
微信扫一扫