みなさん、シェルスクリプト書いていますか?
私は手元環境の更新用にbrew upgrade
やrustup update
などをまとめたシェルスクリプトを書いて定期的に実行していました。
便利にこのスクリプトを叩いていたのですが、linuxとmacで実行するコマンドを変えるなどの込みいったロジックを書こうとするとシェルスクリプトでは力不足を感じます。 また存在しないコマンドは実行しないといった機能や文字をカラフルにしたいなどの機能追加を考えていましたが、これらはあまりシェルスクリプトではやりたくないです。
なので今回はdenoとdenoのライブラリであるdaxを用いてシェルスクリプトを置き換えました。
なぜdenoを使うのか
denoはマルチプラットフォームで動作するJavaScript ランタイムです。ネイティブでTypeScriptをサポートしていることやライブラリを使用するのにあらかじめ別途ライブラリをダウンロードすることが不要などの利点があります。
今回置き換える予定のアップデートスクリプトはmacとlinuxの両方の環境で実行されてほしく、またなるべく事前準備なく実行されてほしいです。
denoはimport $ from "https://deno.land/x/dax/mod.ts"
のようにライブラリの参照元をURLを使って指定することができます。また実行時にローカルに存在していなければ取得をしてくれます。ユーザーはどのライブラリがインストールされているかを気にせずにただ実行すればよいだけです。
dax
daxはdeno上で動くシェルラッパーです。JavaScriptのタグ付きテンプレートを用いて実行することができます。また文字列などはシェルエスケープされて渡されるので安心です。
例えばecho
をしたければ下のように$
を付けたテンプレートリテラルを書くことにより実行することができます。またテンプレートリテラルなので変数も持ち込めます。
import $ from "https://deno.land/x/dax/mod.ts"; const sec = 1; await $`sleep ${sec}`
以下のようにするとdenoとdaxによってhomebrewのアップデートを行うことができます。 便利。
import $ from "https://deno.land/x/dax/mod.ts"; const cmds = [ ["brew", "update"], ["brew", "upgrade"], ]; for (const cmd of cmds) { const result = await $`${cmd}`; }
色を付ける
コマンドの実行結果に色を付けて目立たせたくなったことはありませんか? シェルスクリプトを用いるとANSIコードを使って色付けを有効にしたり無効にしたりと大変です。
シェルスクリプトで行う場合は以下のようにするのが多分良いです。色を無効にしないとずっと色がついて大変。
# ANSIエスケープコードを変数に格納 RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color echo "${GREEN}running $cmd_name${NC}"
cliffyというdenoでCLIツールを作成するライブラリの色付け部分を拝借し色を付けました。
TypeScriptで書かれているのでメソッドチェーンされていてもエディタでの補完が効いて簡単。
import $ from "https://deno.land/x/dax/mod.ts"; import * as cliffy from "https://deno.land/x/cliffy/ansi/colors.ts"; await $`echo ${cliffy.colors.green("this message colored green")}`
完成
daxによるシェル呼び出しとcliffyによるログ色付けを組み合わせるとこんな感じ。
import $ from "https://deno.land/x/dax/mod.ts"; import * as cliffy from "https://deno.land/x/cliffy/ansi/colors.ts"; const cmds = [ ["brew", "upgrade"], ["rustup", "update"] ]; for (const cmd of cmds) { $.log(cliffy.colors.green(`executing ${cmd}`)); await $`${cmd}`; }
このスクリプトをdeno run
で呼び出せばアップデートコマンドを順次呼び出してくれる。
まとめ
今回は使わなかったが、daxにはjsonで返ってきた結果をパースして、TypeScriptの世界でオブジェクトとして扱えるようになる機能などもある。 例えばjqを使ってパズルしている処理もArray.prototype.mapで捌くことができる。
皆さんもお手元のスクリプトを置き換えてみてはいかがでしょうか。