Shell Script Programming using zx
30 Nov 2022
Shell Script Programming w/ No Tool
- Doesn’t support OOP
- Doesn’t support structures like JSON
- Doesn’t support string or array manipulation methods
- Often has to call separate Python or Node.js scripts
Shell Script Programming Scenarios
- Build processes
- CI/CD
- Computer maintenance
Shell Script Programming w/ child_process module
To deal with the problems above, we can do shell script programming in node.js with using child_process module.
child_process
- Node.js built-in module
- Provides the ability to spawn subprocesses
Example: ls -lh /usr
const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
But this method is not developer-friendly.
Shell Script Programming w/ zx
Bash is great, but when it comes to writing more complex scripts, many people prefer a more convenient programming language. JavaScript is a perfect choice, but the Node.js standard library requires additional hassle before using. The zx package provides useful wrappers around child_process, escapes arguments and gives sensible defaults.
How to use zx
npm i -g zx
to install- Write your scripts in a file with an .mjs extension
- Add
#!/usr/bin/env zx
to the beginning of your zx script - Run you script with
zx ./script.mjs
Example: ls -lh /usr
#!/usr/bin/env zx
const data = await $`ls -lh /usr`
console.log(`stdout: ${data}`)
Example: backup repositories from github
#!/usr/bin/env zx
const username = await question('What is your GitHub username? ')
const token = await question('Do you have GitHub token in env? ', {
choices: Object.keys(process.env),
})
let headers = {}
if (process.env[token]) {
headers = {
Authorization: `token ${process.env[token]}`,
}
}
let res = await fetch(
`https://api.github.com/users/${username}/repos?per_page=1000`,
{ headers }
)
const data = await res.json()
const urls = data.map((x) =>
x.git_url.replace('git://github.com/', 'git@github.com:')
)
await $`mkdir -p backups`
cd('./backups')
for (const url of urls) {
await $`git clone ${url}`
}