FORCIA CUBEフォルシアの情報を多面的に発信するブログ

zxを使ってみた

2021.12.17

アドベントカレンダー2021 エンジニア

これは、FORCIA Advent Calendar 2021の17日目の記事です。

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で実行してみても同じ型エラーが発報され実行はされません。

t01_type_anger.png

また、TypeScriptをVScodeで記述する際によく活用される、 "F12を押して関数へジャンプ"がここでも可能です。 以下画像はzxでの$関数へジャンプしている画像です。

t02_jump.png

さらに、以下のように別途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台で曲を繋ぐ練習をしています。

フォルシアではフォルシアに興味をお持ちいただけた方に、社員との面談のご案内をしています。
採用応募の方、まずはカジュアルにお話をしてみたいという方は、お気軽に下記よりご連絡ください。


採用お問い合わせフォーム 募集要項

※ 弊社社員に対する営業行為などはお断りしております。ご希望に沿えない場合がございますので予めご了承ください。