C#でgoogle/zx風にシェルスクリプトを書く

あまりシェルスクリプトを書かない私なのですが(小物でもなんでも書き捨てC#で書くスタイル)、CI だの .NET Core だのなんなので、全く書かないというわけにもいかない昨今です。まぁしかしcmdは嫌だし今更(?)PowerShellもなぁという感じもあり、bashねぇ、とかブツブツ言いながらしょっぱいスクリプトを書く羽目になるわけです。

そこに颯爽と現れたのが google/zx。素敵そうだなーと思いつつJavaScriptを日常的に書くわけでもないのでスルーしてたのですが、こないだもちょっと複雑なシェルスクリプトをJavaScriptで書くで紹介されていて、なるほど色物じゃなくて便利なのか、そうだよね便利だよね!と思い、私は日常的にC#を書くので、C#だったら便利だな、同じ感じで書けるなら、と、思い至ったのでした。

というかまぁzx見て思ったのが、これぐらいの内部DSL、C#でもいけるよ、ということであり……。そして以下のようなものが誕生しました。

image

もともとProcessX - C#でProcessを C# 8.0非同期ストリームで簡単に扱うライブラリというものを公開していたので、更にそれをDSL風味に、zxっぽくシンタックスを弄りました。C# 5.0 async/awaitの拡張性、C# 6.0 using static、C# 6.0 String Interpolation、そしてC# 9.0のTop level statementsと、C#も内部DSLを容易にする構文がどんどん足されています。現在previewのC# 10.0でも、Improvement Interpolated Stringsとして、InterpolatedStringHandlerによって$""の生成時の挙動そのものを生で弄ることが可能になり、よりますます表現のハックが可能になり、色々と期待が持てます。

さて、で、これが使いやすいかというと、見た通りで、使いやすい、です……!stringをawaitしていることに一瞬違和感はめちゃくちゃあるでしょうが、DSLだと思って慣れれば全然自然です(そうか?)。なんか言われてもgoogle/zxなもんです、で逃げれば説得力マシマシになった(そうか?)のが最高ですね。cmd/PowerShell/bashに対する利点は、google/zxの利点と同じように

  • 型が効いてる(C#なので)
  • async/awaitが便利(C#なので)
  • フォーマッタもある(C#なので)
  • エディタ支援が最高(C#なので)

ということで、ぜひぜひお試しください。

csx vs new csproj vs ConsoleAppFramework

C#には.csxという失われしC#スクリプティングな構文が用意されていて、まさに1ファイルでC#の実行が完結するのでこうしたシェルスクリプト風味に最適、と思いきや、実行もエディッティング環境も貧弱で、まさに失われしテクノロジーになっているので、見なかったことにしておきましょう。実際、より良いC#スクリプティング的なシンプルC#の提案が Add Simple C# Programs として出ています(つまりcsxは完全に産廃、NO FUTURE……)。提案(proposed/simple-csharp-pgorams.md)読むと面白いですが、ちょっと少し時間かかりそうですね。

というわけで、csprojとProgram.csの2ファイル構成が良いんじゃないかと思います。ちょっと冗長ではあるけれど、しょーがないね。実行に関しては dotnet run でビルドと実行がその場でできるので、ビルドなしの直接スクリプト実行みたいな雰囲気にはできます。これは普通に便利で、CIとかでもgit pullしている状態のリポジトリ内のスクリプトに対して一行でdotnet run書くだけで動かせるので、非常に良い。こうした .NET Core以降のシンプルになったcsprojとdotnetコマンドの充実から、csxの価値がどんどん消えていったんですねえ。

さて、実際のプロジェクトなどでは、そもそもシェルスクリプト(に限らずバッチなんかも)は一つどころか大量にあったりすることもあるでしょう。そこでCysharpの提供しているCysharp/ConsoleAppFrameworkを使うと、クラスを定義するだけで簡単に実行対象を増やしていけるので、大量のスクリプトの管理を1csprojでまかなうことが可能になります。実行は dotnet run -- foo/bar のようにすればいいだけです。非常におすすめ。シェルスクリプト的なものは、ConsoleAppFramework + ProcessX/zx で書いて回るのは、悪くない選択になると思います。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2022

Twitter:@neuecc GitHub:neuecc

Archive