Meet zx: A Better Way to Write Scripts with Node.js
Tags: node, javascript, productivity
Published: 2021-05-11 20:55:10

---
title: Meet zx: A Better Way to Write Scripts with Node.js
published: true
tags: node, javascript, productivity
canonical_url: https://alexlakatos.com/node/2021/05/11/better-way-to-script/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1vvz1gba5tw8a5pmrwub.png
---

Bash is great and all, but it’s not something I’ll pick up in a day. I was looking for something a little bit more convenient to write scripts in. While looking, I’ve stumbled upon this little [utility from Google](https://github.com/google/zx) called `zx`. And it’s a better way to write scripts using JavaScript.

I thought I’d give `zx` a try. It comes with a bunch of things out of the box, like `chalk` and `fetch`. I know, Node.js already lets me write scripts, but dealing with a bunch of the crap around escaping and sanitizing inputs was painful.

## The Script Way

Before I talk about all the great things `zx` promised, let’s talk about the basics of writing and using scripts first.

Scripts are all text files and need to start with a shebang at the top (also known as sha-bang, hashbang, pound-bang or hash-pling). The shebang tells the operating system to interpret the rest of the file using that interpreter directive, effectively starting the interpreter and passing the text file along as a parameter.

So, when scripts start with `#!/bin/bash` or `#!/bin/sh`, the OS actually runs `$ /bin/bash /path/to/script` behind the scenes every time you execute the script.

Before you can execute the script, you need to declare it in the system as executable. On Unix systems (macOS included), running `$ chmod +x ./script.sh` or `$ chmod 775 ./script.sh` will do the trick.

After you’ve given permissions to your script to be executed, you can run it with `$ ./script.sh`.

## Bash Scripts

A Bash script starts with the bash shebang, followed by a lot of black magic. 😅 For example, to add two numbers that are given as command-line arguments, a script looks like this:

```sh
#!/bin/bash

echo "$1 + $2 = $(($1 + $2))"
```

To run it, save it as `add.sh` and then run the following commands in your Terminal:

```sh
$ chmod +x ./add.sh
$ ./add.sh 5 7
```

The output is going to be `5 + 7 = 12`.

It looks pretty simple if you’ve figured out that `$index` is the command-line argument. I’ve had to look that up while learning shell scripting.

## `zx` Scripts

Before you can use `zx` to run scripts, you’ll need to install it globally via npm, with `$ npm i -g zx`. Why didn’t you need to install `bash`? Because `bash` comes installed by default with Unix systems.

Similarly to all other scripts, a `zx` script will start with a shebang. This time, a little more complicated, the `zx` shebang. Followed by a lot of JavaScript. Let’s try to recreate the above shell script that adds two numbers given as command-line arguments.

```javascript
#!/usr/bin/env zx

console.log(`${process.argv[0]} + ${process.argv[1]} = ${process.argv[0] + process.argv[1]}`)
```

To run it, save it as `add.mjs` and then run the following commands in your Terminal:

```sh
$ chmod +x ./add.mjs
$ ./add.mjs 5 7
```

The output is going to be `/Users/laka/.nvm/versions/node/v16.1.0/bin/node + /usr/local/bin/zx = /Users/laka/.nvm/versions/node/v16.1.0/bin/node/usr/local/bin/zx` 😅. And that’s because `process.argv`, another Node.js wonder, gets called with three extra arguments before you get to 5 and 7. Let’s re-write the script to account for that:

```javascript
#!/usr/bin/env zx

console.log(`${process.argv[3]} + ${process.argv[4]} = ${process.argv[3] + process.argv[4]}`)
```

If you run the script now with `$ ./add.mjs 5 7`, the output is going to be `5 + 7 = 57`. Because JavaScript 🤦. And JavaScript thinks those are strings and concatenates them instead of doing math. Re-writing the script again to deal with numbers instead of strings, it looks like:

```javascript
#!/usr/bin/env zx

console.log(`${process.argv[3]} + ${process.argv[4]} = ${parseInt(process.argv[3], 10) + parseInt(process.argv[4], 10)}`)
```

The Bash script looked a lot cleaner, right? I agree. And if I ever need to add two numbers from the command line, a Bash script would be a way better option! Bash doesn’t shine in a lot of other areas, though. Like parsing JSON files. I gave up trying to figure how to parse JSON files halfway through [the StackOverflow post explaining it](https://stackoverflow.com/questions/1955505/parsing-json-with-unix-tools). But this is where `zx` shines.

I already know how to parse JSON in JavaScript. And here is what the `zx` script for it looks like, using the built-in `fetch` module:

```javascript
#!/usr/bin/env zx

let response = await fetch('https://raw.githubusercontent.com/AlexLakatos/computer-puns/main/puns.json')
if (response.ok) {
    let puns = await response.json()

    let randomPun = Math.floor(Math.random() * puns.length)

    console.log(chalk.red(puns[randomPun].pun))
    console.log(chalk.green(puns[randomPun].punchline))
}
```

Because I was fancy and used the built-in `chalk` module, this `zx` script outputs a random pun from [https://puns.dev](https://puns.dev) in the command-line.

![Computer Pun in Terminal via zx](https://alexlakatos.com/img/posts/node/node-json-zx.png)

Building something similar in `shell` had me rage-quit halfway through the process. And that’s OK. Finding the right tool for the job is what this post was all about.