UNPKG

zx

Version:

A tool for writing better scripts

254 lines (176 loc) 5.09 kB
# 🐚 zx ```js #!/usr/bin/env zx await $`cat package.json | grep name` let branch = await $`git branch --show-current` await $`dep deploy --branch=${branch}` await Promise.all([ $`sleep 1; echo 1`, $`sleep 2; echo 2`, $`sleep 3; echo 3`, ]) let name = 'foo bar' await $`mkdir /tmp/${name}` ``` Bash is great, but when it comes to writing scripts, people usually choose a more convenient programming language. JavaScript is a perfect choice, but standard Node.js library requires additional hassle before using. The `zx` package provides useful wrappers around `child_process`, escapes arguments and gives sensible defaults. ## Install ```bash npm i -g zx ``` ## Documentation Write your scripts in a file with `.mjs` extension in order to be able to use `await` on top level. If you prefer the `.js` extension, wrap your scripts in something like `void async function () {...}()`. Add the following shebang to the beginning of your `zx` scripts: ```bash #!/usr/bin/env zx ``` Now you will be able to run your script like so: ```bash chmod +x ./script.mjs ./script.mjs ``` Or via the `zx` executable: ```bash zx ./script.mjs ``` When using `zx` via the executable or a shebang, all of the functions (`$`, `cd`, `fetch`, etc) are available straight away without any imports. ### ``$`command` `` Executes a given string using the `exec` function from the `child_process` package and returns `Promise<ProcessOutput>`. ```js let count = parseInt(await $`ls -1 | wc -l`) console.log(`Files count: ${count}`) ``` For example, to upload files in parallel: ```js let hosts = [...] await Promise.all(hosts.map(host => $`rsync -azP ./src ${host}:/var/www` )) ``` If the executed program returns a non-zero exit code, `ProcessOutput` will be thrown. ```js try { await $`exit 1` } catch (p) { console.log(`Exit code: ${p.exitCode}`) console.log(`Error: ${p.stderr}`) } ``` ### `ProcessOutput` ```ts class ProcessOutput { readonly exitCode: number readonly stdout: string readonly stderr: string toString(): string } ``` ### `cd()` Changes the current working directory. ```js cd('/tmp') await $`pwd` // outputs /tmp ``` ### `fetch()` A wrapper around the [node-fetch](https://www.npmjs.com/package/node-fetch) package. ```js let resp = await fetch('http://wttr.in') if (resp.ok) { console.log(await resp.text()) } ``` ### `question()` A wrapper around the [readline](https://nodejs.org/api/readline.html) package. ```ts type QuestionOptions = { choices: string[] } function question(query?: string, options?: QuestionOptions): Promise<string> ``` Usage: ```js let username = await question('What is your username? ') let token = await question('Choose env variable: ', { choices: Object.keys(process.env) }) ``` ### `sleep()` A wrapper around the `setTimeout` function. ```ts function sleep(ms: number): Promise<void> ``` Usage: ```js await sleep(1000) ``` ### `chalk` package The [chalk](https://www.npmjs.com/package/chalk) package is available without importing inside scripts. ```js console.log(chalk.blue('Hello world!')) ``` ### `fs` package The [fs](https://nodejs.org/api/fs.html) package is available without importing inside scripts. It is asyncronous by default. ```js let content = await fs.readFile('./package.json') ``` ### `os` package The [os](https://nodejs.org/api/os.html) package is available without importing inside scripts. ```js await $`cd ${os.homedir()} && mkdir example` ``` ### `$.shell` Specifies what shell is used. Default is `which bash`. ```js $.shell = '/usr/bin/bash' ``` ### `$.prefix` Specifies the command what will be prefixed to all commands run. Default is `set -euo pipefail;`. ### `$.quote` Specifies a function what will be used for escaping special characters during command substitution. Default is the [shq](https://www.npmjs.com/package/shq) package. ### `$.verbose` Specifies verbosity. Default is `true`. Verbose mode prints all executed commands along with their outputs. The is the same as using `set -x` in Bash. ### `__filename` & `__dirname` In [ESM](https://nodejs.org/api/esm.html) modules, Node.js does not provide `__filename` and `__dirname` globals. As such globals are really handy in scripts, `zx` provides these for use in `.mjs` files (when using the `zx` executable). ### `require` In [ESM](https://nodejs.org/api/modules.html#modules_module_createrequire_filename) `require` is not defined, but sometimes it's convenient to have this legacy api in global. ```js require('./version.js') ``` ### Importing from other scripts It is possible to make use of `$` and other functions via explicit imports: ```js #!/usr/bin/env node import {$} from 'zx' await $`date` ``` ### Passing env variables ```js process.env.FOO = 'bar' await $`echo $FOO` ``` ### Executing remote scripts If the argument to the `zx` executable starts with `https://`, the file will be downloaded and executed. ```bash zx https://medv.io/example-script.mjs ``` ## License [Apache-2.0](LICENSE) Disclaimer: _This is not an officially supported Google product._