2022.04.08

JavaScriptでシェルを書こう ——zxのご紹介

こんにちは。次世代システム研究室の H.U と申します。

今回は、JavaScript でシェルを書ける Google 製のツール、「zx」を紹介します。

初めに

みなさん、シェルスクリプトは好きですか?

シェルスクリプトは便利です。

シンプルな処理ならば簡潔に書けますし、動く環境も多いです。

 

しかし、条件分岐やループなどの少し込み入った処理などを書こうとすると、どうにも癖があります。

また、JSON をパースしたり、配列を扱ったりするのも、あまり得意ではありません。

それならばより慣れ親しんだ便利な言語で書きたいという気持ちは自然と湧いてきます。

 

しかし、使用したい言語によってはセットアップが面倒という課題もあります。

この点について、GitHub の zx のリポジトリでは下記のように説明がされています。

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.

セットアップ

zx のインストールは、npm で行います。

リポジトリの README の記述に従い、グローバルインストールをします。
npm -i -g zx
ちなみに私の環境は、上記手順でインストールしたzxのパッケージも含め、このようになります。
  • macOS: 11.5.2
  • Node.js: v14.17.6
  • zx: 6.0.7
GitHubの zx のリポジトリには「Requirement: Node version >= 16.0.0」とありますが、私の環境でも動作しました。

ただし、zx は Top-Level Await を使用するため、同機能がサポートされているバージョン(v14.8.0以降)でないと動作しませんのでご注意ください。

実践①

それではさっそく、zx を使っていきましょう。

zx では Top-Level Await を使用するため、スクリプトのファイルの拡張子は .mjs になります。

またスクリプトの先頭には、zx の shebang を書く必要があります。

 

初めに、「Hello World」をしてみます。

$command という形で、シェルのコマンドを実行できます。

 
#!/usr/bin/env zx
const msg = "Hello, world!";
$`echo ${msg}`;
出力結果はこちらです。
$ echo 'Hello, world!'
Hello, world!
 

なお、実行コマンドが表示されないようにするためには、このようにコードを書きます

※`pipe(process.stdout)`がないと、何も表示されなくなってしまいます。
#!/usr/bin/env zx
$.verbose = false;
const msg = "Hello, world!";
$`echo ${msg}`.pipe(process.stdout);
 

また、Promise.allを使用し、並列処理を簡単に書けます。
#!/usr/bin/env zx

$.verbose = false;

await Promise.all([
  $`echo 1`.pipe(process.stdout),
  $`echo 2`.pipe(process.stdout),
  $`echo 3`.pipe(process.stdout),
]);
出力結果はこちらです。
1
2
3

実践②

それでは「初めに」で記載した条件分岐を実践してみましょう。

今回は、条件によって表示するメッセージを変更するシェルを書いてみます。

ついでなので、オブジェクトも扱ってみます。

 

zx では、JavaScript のようにif文を記述できます。

また、readline の wrapper として question 関数が使えるため、簡単に対話形式のシェルが書けます。

下記のプログラムでは、年齢を訊ね、20歳以上であれば「お酒が飲めるね」、そうでなければ「ごめんなさい、お酒は飲めません」と表示します。
#!/usr/bin/env zx

$.verbose = false;

const msg = {
  good: "You can drink! :D",
  notgood: "Sorry..., you cannot drink :(",
};

const age = await question("How old are you? ");

if (age >= 20) {
  await $`echo ${msg.good}`.pipe(process.stdout);
} else {
  await $`echo ${msg.notgood}'`.pipe(process.stdout);
}
出力結果はこちらです。
How old are you? 22
You can drink! :D

How old are you? 19
Sorry..., you cannot drink :(
 

また、リポジトリでも説明がありますが、Node.js に標準で搭載された API 以外にも、zx では一部のパッケージを import なしで利用できます。

せっかくなのでそちらも試していきたいと思います。

 

今回使用するのは、「chalk」というパッケージです。

こちらを使用することで、ターミナルに表示する文字の装飾ができます。

黒板にチョークで書くということですね。小学生の時分、色付きのチョークにやたら憧憬を抱いたことを思い出します。

余談はさておき、ソースコードは下記のようになります。

こちらではついでに、配列や繰り返し処理を扱ってみます。

 
#!/usr/bin/env zx

$.verbose = false;

const colors = [
  "#FFFF00",
  "#DBFF24",
  "#B7FF48",
  "#8AFF75",
  "#69FF96",
  "#48FFB7",
  "#27FFD8",
  "#00FFFF",
];
const firstMsg = "Introduction zx";
const msg = "グラデーションしてます!";

console.log(chalk.redBright(firstMsg));

colors.map((c) => {
  console.log(chalk.hex(c)(msg));
});
 

今回の実行結果は、スクリーンショットで確認していきましょう。

このように、表示される文字が黄色から水色に、徐々にグラデーションしてくれました。

まとめ

「セットアップ」の節で記載したように Node.js および npm は必要ですが、そこさえクリアできれば、簡単に、リッチなシェル環境ができる zx は魅力的だな、と感じました。

また本記事のなかでは詳しく触れていませんが、リポジトリの README によると TypeScript での記述もできるようです。

型の恩恵が受けられるのは、シェルスクリプトの代替として使われることのあるPythonやPerlに対する優位性となるのではないでしょうか。

 

昨今は、エコシステムの発展もあり JavaScript の人気は高まっています。

その活用法の1パターンとして、今回は zx を紹介いたしました。

ご参考になれば幸いです。

 

次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。

 

参考

google/zx

JavaScriptが1位、2021年の開発者に人気なプログラミング言語

 

※アイキャッチ画像は「いらすとや」様のものを使用しております

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事