zxを使ってみた
zx とは
zxはNodeのchild_process
のラッパーで、JavaScriptで記述したスクリプトをNodeで実行し、
shellコマンドを発行できます。
一言で表すと、お手軽にJavaScriptで記述し、実行できるshellです。
googleから公開され、2021年初頭に話題になりました。(google/zx: https://github.com/google/zx)
筆者は普段からスクリプトはbashで実行している一方、業務で使い慣れているTypeScriptの型をzxで使えるとマニュアルで見かけ、使ってみました。
zx の導入
zxを利用するには、まず、Nodeのバージョンが14.13.1以上である必要があります。
Nodeの準備が整っていれば、以下でインストールします。
npm i -g zx
zxで実行するスクリプトは、top-level-awaitを利用すべく、.mjs
拡張子での作成が推奨されています。
.js
で作成する場合、コマンドの発行部分でvoid async function(){...}()
と記述する必要があり、少し長くなってしまいます。
zx の使用
以下でいくつかzxの使用方法を解説します。
コマンドの発行
zxで利用するスクリプトでは、先頭に#!/usr/bin/env zx
を記載し、それ以下へ処理を記述していきます。
また、以下のcommandの部分へ、shellで発行したいコマンドを記述します。
$`command`
以下はhello worldするスクリプトです。
変数の記述はJavaScriptで見慣れた記法そのものです。
#!/usr/bin/env zx
const word = "hello world";
await $`echo ${word}`;
作成したスクリプトは、以下のように実行します。
zx ./hello.mjs
$ echo $'hello world'
hello world
zx ./hello.mjs
で実行した結果、$ echo $'hello world'
とコマンドが出力された後、hello world
がechoされていることが分かります。コマンドおよび実行結果をコンソールへ出力させない場合、
zx ./hello.mjs --quiet
と実行することで、表示させないことができます。
関数/パッケージの利用
追加でインストールせずとも利用できる関数/パッケージについて一部紹介します。
- sleep
JavaScriptでのsetTimeout
をラップした関数です。以下ではneruをechoし、1秒待った後okita
がechoされます
#!/usr/bin/env zx
$`echo neru`;
await sleep(1000);
$`echo okita`;
zx ./wakeup.mjs
$ echo neru
neru
$ echo okita
okita
- fetch
zxではnode-fetchをラップしたfetch関数を以下のように利用できます。
#!/usr/bin/env zx
const resp = await fetch("https://www.forcia.com/");
console.log(resp.ok);
zx ./fetchForcia.mjs
$ fetch https://www.forcia.com/
true
TypeScript で書いてみる
筆者は業務において、JavaScriptを生で記述する機会はほとんどなく、TypeScriptを使用しています。
zxのマニュアルにも記載がありますが、zxのスクリプトをTypeScriptで記述し、実行してみます。
まず、実行するにはts-node
,typescript
が必要なので、インストールします。
npm i -g ts-node
npm i -g typescript
hello worldするスクリプトは以下のように記述できます。
#!/usr/bin/env zx
import "zx/globals";
const word: string = "hello world";
void (async function () {
await $`echo ${word}`;
})();
ここでは以下で実行します。
ts-node tsZx.ts
$ echo $'hello world'
hello world
TypeScriptでの記述なので以下のような不正な記述に対し、警告を発報してくれます。
もちろんts-nodeで実行してみても同じ型エラーが発報され実行はされません。
また、TypeScriptをVScodeで記述する際によく活用される、 "F12を押して関数へジャンプ"がここでも可能です。
以下画像はzxでの$
関数へジャンプしている画像です。
さらに、以下のように別途index.d.ts
を用意します。
interface Hello {
word: string;
language: string;
}
export type HelloWords = Hello[];
それをzxで実行するスクリプトへimportして型を利用できます。
#!/usr/bin/env zx
import "zx/globals";
import { HelloWords } from ".";
const words: HelloWords = [
{
word: "こんにちは",
language: "Japanese",
},
{
word: "Hello",
language: "English",
}
];
Promise.all(
words.map(w => {
$`echo word: ${w.word} language: ${w.language}`;
})
);
ts-node tsZxType.ts
$ echo word: $'こんにちは' language: Japanese
$ echo word: Hello language: English
word: こんにちは language: Japanese
word: Hello language: English
筆者はShellよりもTypeScriptでの記述の方が親しみがあるためか、
zxでの記述が見通しがいいように感じられます。
使ってみた感想
慣れた方は普通にShellで書いた方が早そうではありますが、JavaScriptに親しみがある方は使用してみてもいいかもしれません。
TypsScriptの実行環境を整える手間はありますが、TypeScriptの便利さをShellでも再現できたのには感動しました。
この記事を書いた人
田中柾伎
2020年1月キャリア入社エンジニア
最近、ターンテーブル2台で曲を繋ぐ練習をしています。
Discussion