devops

Discovering zx: A Good Choice to Replace Bash Scripts

Kaitou
Kaitou

What is zx?

zx is a tool from Google that lets you write shell scripts using JavaScript (or TypeScript). If you’ve ever struggled with Bash’s quirks or wished you could use modern programming features in your scripts, zx is good way for you.

https://github.com/google/zx


Why I Switched: My Scripting Journey

For years, I used Bash for all my automation because of its simplicity and power when working with Linux-related tasks. ? But as my scripts grew larger and more complex, managing them became a headache. That's when I discovered zx—and every become more simple again. ?


The Good: Why I Like zx

  • Familiar JavaScript Syntax: Use variables, loops, async/await, and all the logic you know from JS.
  • Error Handling: One of the difficult problems with shell scripts is error handling and bug tracing. With try/catch makes it easy to handle failures gracefully.
  • NPM Ecosystem: Import any npm package—fetch APIs, parse YAML, color your output, and more.
  • Cross-Platform: Works on Linux, macOS, and Windows.
  • Readable and Maintainable: Scripts are easier to read, debug, and extend.
  • Built-in Utilities: Functions like cd(), question(), and libraries like chalk and fs-extra are ready to use.
  • Top-level await: No need to wrap everything in async functions—just use await at the top level.
  • Great for APIs and Modern Workflows: Fetch data, parse JSON, and interact with web services easily.

The Bad: Where zx Falls Short

  • Requires Node.js: Not available by default on all systems (unlike Bash). It is good with systems that have Nodejs available.
  • Performance: For tiny, one-off scripts, Bash can be faster to start.
  • Shell Command Differences: Some shell commands behave differently across OSes—be careful if you want true portability.
  • Learning Curve: If you’re new to JavaScript, there’s a bit to learn.
  • Not Always the Best for Text Processing: Bash’s built-in tools like awk and sed are still unbeatable for some quick text hacks -- I had some trouble using the sed command to replace text. It automatically inserted a $ character for no apparent reason. It might be a legacy bug. So I had to use fs instead.

zx vs Bash: A Deep Dive Comparison

Feature zx (JavaScript) Bash (Shell)
Syntax Modern JS Shell scripting
Error Handling try/catch Exit codes, traps
Libraries NPM ecosystem Built-in Unix tools
Portability Node.js required Native on Unix
Readability High (for JS devs) Varies
Async Support Yes (async/await) No (workarounds)
Text Processing JS methods, NPM awk, sed, grep
User Input question() read
Debugging JS tools, console.log echo, set -x
Community JS/NPM ecosystem Huge, mature

When to use zx:

  • You want to use JS logic, npm packages, or async code.
  • Your script is more than a few lines or needs to be maintained.
  • You want better error handling and readability.
  • You need to interact with APIs or do complex logic.

When to use Bash:

  • You need a quick, portable one-liner.
  • You’re working in a minimal environment (no Node.js).
  • You need advanced text processing with classic Unix tools.
  • You want maximum compatibility with existing shell scripts.

Getting Started with zx

Prerequisites

  • Node.js v14.13.1 or higher
  • Some basic JavaScript knowledge

Installation

Install globally for quick scripts:

npm install -g zx

Or locally in your project for version control:

npm install --save-dev zx

Your First zx Script

Create a file called hello.mjs:

#!/usr/bin/env zx
$.verbose = true // comment it out if you don't want too much log
await $`docker run hello-world`
console.info("command executed successfully")

Make it executable and run it:

chmod +x hello.mjs
./hello.mjs

01


Example: Automate Project Setup with zx

Here’s a real-world example: a script to bootstrap a new Node.js project directory.

#!/usr/bin/env zx
import { $, question, cd, fs, chalk } from 'zx'

const dir = await question('Project directory name? ')
await fs.mkdir(dir)
cd(dir)
await $`git init`
await $`npm init -y`
const pkgs = (await question('Packages to install (space separated)? ')).split(' ').filter(Boolean)
if (pkgs.length) await $`npm install ${pkgs}`
await fs.writeFile('README.md', `# ${dir}\n`)
console.log(chalk.green('Project setup complete!'))

How to run:

  1. Save as setup.mjs and make it executable: chmod +x setup.mjs
  2. Run: ./setup.mjs

02

How zx Makes Scripting Easier (with Examples)

Use Variables and Logic

const files = ['foo.txt', 'bar.txt']
for (const file of files) {
  await $`echo "Hello" > ${file}`
}

Fetch Data from APIs

import fetch from 'node-fetch'
const res = await fetch('https://api.github.com/repos/google/zx')
const data = await res.json()
console.log('Stars:', data.stargazers_count)

Handle Errors Gracefully

try {
  await $`cat notfound.txt`
} catch (err) {
  console.error('File not found!')
}

Pro Tips for zx Users

  • Turn off verbose mode: $.verbose = false for cleaner output.
  • Use npm packages: Need to parse YAML, fetch APIs, or color output? Just import the package.
  • Mix and match: Call Bash scripts from zx, or use zx for logic and Bash for quick text processing.
  • Debug easily: Use console.log or a Node.js debugger.
  • Cross-platform caution: Test your scripts on all target OSes if portability matters.
  • Use built-in utilities: cd(), question(), and more are available out of the box.
  • Leverage minimist: Command-line arguments are parsed for you as argv.

Creative Ways to Use zx

  • Use zx to automate repetitive dev tasks: deploys, backups, or even generating reports.
  • Combine zx with APIs: fetch data, process it, and output results—all in one script.
  • Build interactive CLI tools with prompts and colored output.
  • Replace fragile Bash scripts in your CI/CD pipelines with zx for better maintainability.
  • Use zx for cross-platform automation where Bash scripts would need lots of tweaks.
  • Build scripts that send notifications, update dashboards, or even tweet!

Final Thoughts

Switching to zx made my scripting life easier, especially for anything more complex than a one-liner. If you’re a developer who loves JavaScript, give zx a try—you might never look back!

Happy scripting!