LIVEReading: Run Shell Commands from JavaScript Without the PainTotal time: 10 minSteps: 6Worked first time: 72% LIVEReading: Run Shell Commands from JavaScript Without the PainTotal time: 10 minSteps: 6Worked first time: 72%
CBW
Run Shell Commands from JavaScript Without the Pain
Mediumgithub.com/google/zx2026-06-17

Run Shell Commands from JavaScript Without the Pain

zx lets you write shell scripts using JavaScript instead of bash. Run commands, chain them together, and handle output — all without learning bash syntax.

// Build stats

  • Total time10 min
  • Number of steps6
  • DifficultyMedium
  • Worked first time72%
// Before you start

What you need

  • Node.js 18 or later installed (check with: node --version)
  • npm installed (comes with Node.js)
  • A terminal (Terminal on Mac, Command Prompt or PowerShell on Windows)
  • Basic comfort running commands in a terminal
01
Step 1 of 6

Create a project folder

1 min

We need a clean folder to work in. This keeps your zx scripts organized and separate from other files on your computer.

Terminal · mac
$ mkdir my-zx-scripts && cd my-zx-scripts && npm init -y
What you should see
A package.json file is created. You'll see output like: 'Wrote to .../my-zx-scripts/package.json'
This might happen

'npm init' asks questions interactively on some setups

The -y flag skips all questions. If you still see prompts, just press Enter through each one.

02
Step 2 of 6

Install zx

1 min

This downloads the zx package into your project folder. After this, you can use zx to run any script in this folder.

Terminal · mac
$ npm install zx
What you should see
You'll see npm downloading packages, ending with something like: 'added 12 packages in 3s'
This might happen

Slow or stalled install

Check your internet connection. If it hangs for more than 2 minutes, press Ctrl+C and try again.

03
Step 3 of 6

Enable ES modules in your project

1 min

zx uses modern JavaScript module syntax. You need to tell Node.js to expect that. This one command edits your package.json to flip the right switch.

Terminal · mac
$ node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); p.type='module'; fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
What you should see
No output means success. Open package.json and you should see a line: "type": "module"
This might happen

You see a syntax error or 'Cannot find module' message

Make sure you are inside the my-zx-scripts folder (run: pwd on Mac/Linux or cd on Windows to confirm). Then try the command again.

04
Step 4 of 6

Write your first zx script

2 min

Now you'll create a script file. This example lists files in your current folder, prints today's date, and shows a greeting — all using shell commands written in JavaScript. The $` ` syntax is how zx runs a shell command.

Terminal · mac
$ cat > hello.mjs << 'EOF'
$ import { $ } from 'zx'
$
$ const date = await $`date`
$ console.log('Today is:', date.stdout.trim())
$
$ const files = await $`ls`
$ console.log('Files here:\n', files.stdout.trim())
$ EOF
What you should see
A file called hello.mjs appears in your folder. No other output.
This might happen

On Windows, the cat command does not exist in Command Prompt

Use Git Bash or WSL (Windows Subsystem for Linux) to run this command, or manually create hello.mjs in Notepad with the same content shown above.

05
Step 5 of 6

Run your script

1 min

This runs your script through zx. It will execute the shell commands inside and print the results. You should see today's date and a list of files in your folder.

Terminal · mac
$ npx zx hello.mjs
What you should see
Something like: Today is: Mon Jan 20 10:30:00 UTC 2025 Files here: hello.mjs node_modules package.json
This might happen

Error: 'date is not recognized' on Windows

Windows uses a different date command. Replace `date` in the script with `echo %DATE%` or run the script inside Git Bash or WSL where standard Unix commands work.

06
Step 6 of 6

Try a more useful real-world script

3 min

This script runs two shell commands in parallel — something that is genuinely hard to do in plain bash. It shows how zx handles timing and combines results. This is the kind of task zx was built for.

Terminal · mac
$ cat > parallel.mjs << 'EOF'
$ import { $ } from 'zx'
$
$ console.log('Running two tasks at the same time...')
$
$ const [a, b] = await Promise.all([
$ $`sleep 1; echo 'Task A done'`,
$ $`sleep 1; echo 'Task B done'`,
$ ])
$
$ console.log(a.stdout.trim())
$ console.log(b.stdout.trim())
$ console.log('Both finished!')
$ EOF
$ npx zx parallel.mjs
What you should see
Running two tasks at the same time... Task A done Task B done Both finished! (Both tasks finish in about 1 second total, not 2.)
This might happen

'sleep' is not recognized on Windows Command Prompt

Run inside Git Bash or WSL. Alternatively replace `sleep 1` with `timeout /t 1 /nobreak > NUL` but this only works in PowerShell, not inside zx's shell on Windows.

// Status

cooked. baked. worked.

A working local setup where you can run .mjs script files that execute shell commands using JavaScript. You have two working example scripts and can modify them to automate your own tasks.

// the honest bit

The honest part

zx is a scripting tool, not a no-code tool — you are still writing JavaScript. A non-developer can copy and modify these examples, but building anything custom means editing the .mjs files. Windows support works best inside Git Bash or WSL; native Command Prompt has real friction. zx does not replace bash for simple one-liners, but it shines when you need logic, loops, or parallel tasks.