π zx
A Deno version of https://github.com/google/zx
#!/usr/bin/env zx
await $`cat package.json | grep name`
let branch = await $`git branch --show-current`
await <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">β</mi><mi>d</mi><mi>e</mi><mi>p</mi><mi>d</mi><mi>e</mi><mi>p</mi><mi>l</mi><mi>o</mi><mi>y</mi><mo>β</mo><mo>β</mo><mi>b</mi><mi>r</mi><mi>a</mi><mi>n</mi><mi>c</mi><mi>h</mi><mo>=</mo></mrow><annotation encoding="application/x-tex">`dep deploy --branch=</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord">β</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">p</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.01968em;">pl</span><span class="mord mathnormal" style="margin-right:0.03588em;">oy</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em;"></span><span class="mord">β</span><span class="mord mathnormal">b</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">an</span><span class="mord mathnormal">c</span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>{branch}`
await Promise.all([
$`sleep 1; echo 1`,
$`sleep 2; echo 2`,
$`sleep 3; echo 3`,
])
let name = 'foo bar'
await <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">β</mi><mi>m</mi><mi>k</mi><mi>d</mi><mi>i</mi><mi>r</mi><mi mathvariant="normal">/</mi><mi>t</mi><mi>m</mi><mi>p</mi><mi mathvariant="normal">/</mi></mrow><annotation encoding="application/x-tex">`mkdir /tmp/</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">β</span><span class="mord mathnormal" style="margin-right:0.03148em;">mk</span><span class="mord mathnormal">d</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">/</span><span class="mord mathnormal">t</span><span class="mord mathnormal">m</span><span class="mord mathnormal">p</span><span class="mord">/</span></span></span></span>{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. zx
package provides
useful wrappers around Deno.run
, escapes arguments and
gives sensible defaults.
Install
deno install -A --unstable https://deno.land/x/zx_deno/mod.mjs
Required permissions:
--allow-run
to allow running commands--allow-read
toawait import
the script to run--allow-env
for subprocess to work correctly
Not that scripts permissions inherits zx
permissions
Documentation
Write your scripts in a file with .mjs
extension in order to
be able to use await
on top level. If you prefer .js
extension,
wrap your script in something like void async function () {...}()
.
Add next shebang at the beginning of your script:
#!/usr/bin/env zx
Now you will be able to run your script as:
chmod +x ./script.mjs
./script.mjs
Or via zx
bin:
zx ./script.mjs
Then using zx
bin or via shebang, all $
, cd
, fetch
, etc
available without imports.
$`command`
Executes given string using Deno.run
function and returns Promise<ProcessOutput>
.
let count = parseInt(await $`ls -1 | wc -l`)
console.log(`Files count: ${count}`)
Example. Upload files in parallel:
let hosts = [...]
await Promise.all(hosts.map(host =>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">β</mi><mi>r</mi><mi>s</mi><mi>y</mi><mi>n</mi><mi>c</mi><mo>β</mo><mi>a</mi><mi>z</mi><mi>P</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">/</mi><mi>s</mi><mi>r</mi><mi>c</mi></mrow><annotation encoding="application/x-tex">`rsync -azP ./src </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord">β</span><span class="mord mathnormal" style="margin-right:0.03588em;">rsy</span><span class="mord mathnormal">n</span><span class="mord mathnormal">c</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord">./</span><span class="mord mathnormal">src</span></span></span></span>{host}:/var/www`
))
If executed program returns non-zero exit code, ProcessOutput
will be thrown.
try {
await $`exit 1`
} catch (p) {
console.log(`Exit code: ${p.exitCode}`)
console.log(`Error: ${p.stderr}`)
}
ProcessOutput
class ProcessOutput {
readonly exitCode: number
readonly stdout: string
readonly stderr: string
toString(): string
}
cd()
Changes working directory.
cd('/tmp')
await $`pwd` // outputs /tmp
fetch()
The native fetch()
function provided by Deno
let resp = await fetch('http://wttr.in')
if (resp.ok) {
console.log(await resp.text())
}
question()
This is a wrapper around window.prompt
. As of now, the option
parameter is ignored.
type QuestionOptions = { choices: string[] }
function question(query: string, options?: QuestionOptions): Promise<string>
Usage:
let username = await question('What is your username? ')
let token = await question('Choose env variable: ', {
choices: Object.keys(Deno.env.toObject())
})
chalk
package
The chalk package provided in the original zx
package is replaced by the standard https://deno.land/std/fmt/colors.ts
module
console.log(colors.blue('Hello world!'))
fs
package
The fs module available without importing inside scripts.
let content = await fs.readFile('./package.json')
Promisified version imported by default. Same as if you write:
import {promises as fs} from 'https://deno.land/std@0.95.0/node/fs.ts'
os
package
The os package available without importing inside scripts.
await <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">β</mi><mi>c</mi><mi>d</mi></mrow><annotation encoding="application/x-tex">`cd </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">β</span><span class="mord mathnormal">c</span><span class="mord mathnormal">d</span></span></span></span>{os.homedir()} && mkdir example`
$.shell
Specifies what shell is used. Default is /bin/bash
.
$.shell = '/usr/bin/bash'
$.verbose
Specifies verbosity. Default: true
.
In verbose mode prints executed commands with outputs of it. Same as
set -x
in bash.
Importing from other scripts
Itβs possible to use $
and others with explicit import.
#!/usr/bin/env node
import {$} from 'https://deno.land/x/zx_deno/mod.mjs'
await $`date`
Passing env variables
Deno.env.set('FOO', 'bar')
await <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">β</mi><mi>e</mi><mi>c</mi><mi>h</mi><mi>o</mi></mrow><annotation encoding="application/x-tex">`echo </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">β</span><span class="mord mathnormal">ec</span><span class="mord mathnormal">h</span><span class="mord mathnormal">o</span></span></span></span>FOO`
Executing remote scripts
If arg to zx
bin starts with https://
, it will be downloaded and executed.
zx https://medv.io/example-script.mjs