どうも。CX事業本部Delivery部のえーたん(@eetann092)です。
zxが面白そうだったので素振りしました。 zxのドキュメントを読んで特に気になったった部分を例とともに紹介します。
zxとは?
zxを使うと、JavaScriptのファイルとしてシェルスクリプトを書くことができます。
以下はzx
を使わずにchild_process
を使って書いた例です。
import { exec } from "child_process";
exec('ls', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
console.log(`${stdout}`);
console.error(`${stderr}`);
});
実行結果の例は以下です。
README.md
node_modules
package-lock.json
package.json
src
参考:Child process | Node.js v18.7.0 Documentation
次に、zx
を使った例です。 $とバッククオートを使ってコマンドを書きます。
import 'zx/globals'
await $`ls`
zxを使えば、標準出力ためにコールバックやconsole.log
を書く必要がなく、コードが短くなります。
$ ls
README.md
node_modules
package-lock.json
package.json
src
実行コマンドがわかる
実行したコマンドが何かを出力してくれます。
以下、コマンド実行例のためのスクリプトです。
import 'zx/globals'
await $`git checkout $(echo "$(echo "$(git --no-pager branch -vv)" | fzf +m)" | awk '{print $1}' | sed "s/.* //")`;
以下が出力です。
$ git checkout ……
というように、どんなコマンドを実行したのかが表示されます。
また、色つきであるため、自分でいちいちconsole.log("$ git checkout ……")
のようにコードを書くよりも分かりやすいです。
どんなコマンドが実行されたかを表示させたくない場合、以下のように$
をいじります。
$.verbose = false
出力を非表示にしたい
echo ${content} | pbcopy
のように、変数展開をしたいときもあると思います。
変数の中の文字列が長くて出力をしたくない場合は、以下のように最後にquiet()
をつけます。
await $`echo ${content} | pbcopy`.quiet()
TypeScriptで実行したい
TypeScriptで書かれたファイルをzxで実行したい場合、node --loader ts-node/esm hoge.ts
のようにts-node経由で実行します。
import matter from "gray-matter";
import "zx/globals";
function devideMetaInfo(file: string): matter.GrayMatterFile<string> {
return matter.read(file);
}
async function sendToClipboard(content: string) {
// Mac
await $`echo ${content} | pbcopy`.quiet();
}
void (async function () {
const metaInfo = devideMetaInfo("./sample.md");
await sendToClipboard(metaInfo.content);
})();
node --loader ts-node/esm src/zx-md-copy.ts
パイプラインの区切りを明確にしたい
以下のように.pipe
を生やせばパイプラインを使えます。
import "zx/globals";
const branches = await $`git --no-pager branch -vv`;
const branch = await $`echo "${branches}"`.pipe($`fzf +m`);
const branchName = await $`echo "${branch}"`
.pipe($`awk '{print $1}'`)
.pipe($`sed "s/.* //"`);
await $`git checkout ${branchName}`;
短いコマンドであれば1行で書いたほうが良いかもしれませんが、複数のパイプラインを使う場合は.pipe
を使えば区切りが明確になります。
参考:zx/process-promise.md at main · google/zx
オプションがたくさんあるので配列に入れたい
zxでは、配列は勝手にスペースで連携して展開してくれます。オプションがたくさんある時に便利そうです。
import "zx/globals";
const options = [
"s3://mybucket",
"--recursive",
"--human-readable",
"--summarize",
];
await $`aws s3 ls ${options}`;
色をつけたい
zxでは、chalkを内蔵しています。chalkを使うことで、簡単に出力に色を付けることができます。
import "zx/globals";
let name = await question('名前を入力してね:')
echo`Hello, ${name}`
echo(chalk.greenBright(`Hey, ${name}!`))
echo(chalk.bgGreenBright.black(`Oh, ${name}!`))
echo(chalk.underline(`ここはテストに出ますよ、${name}`))
chalk.[....](string, [string...])
の形式で、簡単にスタイルを設定できます。
また、上記の例の通りzxではquestion
を使ってユーザーの入力を取得したり、echo()
でconsole.log
のように出力が可能です。
スクリプトの引数を使いたい
zxではminimistを内蔵しています。
argv
変数から引数にアクセスできます。
import "zx/globals";
if (argv.h) {
echo`hoge hoge`
}
if (argv.f) {
echo`foo`
}
if (argv.b) {
echo`bar`
}
let name = argv.name;
echo`Hello, ${name}`
echo(chalk.underline.bgYellow.black("引数全部表示する"))
echo(argv._)
npx zx src/zx-argv.mjs -hb --name="Kerry" aaaaa bbbb ccccc
実行中であることをspinnerを使って表示
zxにはspinner()
があります。ぐるぐるします。
import "zx/globals";
import { spinner } from 'zx/experimental'
await spinner('working...', () => $`sleep 3`)
echo(chalk.bgGreen.black("Finish!"))
Markdownのコードブロックに書かれたスクリプトを実行
zxにはMarkdownのコードブロックに書かれたスクリプトを実行する機能もあります。 コードよりもドキュメント量の方が多くなる時に便利そうです。
まず、以下がサンプルのMarkdownファイルです。
script.md
このMarkdownファイルのコードブロックの内容が実行できる。
```js
// コードブロックの言語指定はjsにした
echo`directory: ${__dirname}`
echo`finename: ${__filename}`
```
# 言語指定
```
console.log('言語指定をしていないコードブロック')
console.log('実行されないよ!')
```
```bash
# コードブロックの言語指定はbashにした
echo "bashbashbash"
```
# 別のコードブロックの変数
別のコードブロックの変数も使える。
```js
const bar = 'Kerry'
const foo = 'HeyHey'
```
```js
echo`${foo}, ${bar}`
```
↓実行するためのコマンドです。
npx zx src/script.md
実行結果は以下です。
directory: /Users/kerry/ghq/github.com/eetann/suburi-zx/src
finename: /Users/kerry/ghq/github.com/eetann/suburi-zx/src/script.md
$
> # コードブロックの言語指定はbashにした
> echo "bashbashbash"
>
bashbashbash
HeyHey, Kerry
コードブロックには言語指定としてjs
、bash
、sh
のいずれかが必要です。また、言語指定にbash
、sh
を書いた場合は、表示が異なるようです。以下に該当する部分を再度表示します。
$
> # コードブロックの言語指定はbashにした
> echo "bashbashbash"
>
先頭の文字が変わったり、コメントアウトも表示するようです。
${__dirname}
や${__filename}
のような変数も使えます。
リンク集
今回使用したサンプルのコードは以下のリポジトリにあります。
参考