MS_BuildingYourOwnCLI - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki
昨今、以下の理由で、CUI(CLI)への回帰現象がある。
- クロスプラットフォーム対応(と言うかLinux対応)
-
Visual Studio Code などのエディタ系を IDE 化する際、
最も簡潔な連携インターフェイスとして CLI が選択されている。 - LLM 系のチャット UI(GUI)の隆盛により、
昨今の操作性の上がった CUI の方が操作性が優れるという逆転現象が発生。
補足(「回帰」の背景): 原文の見立ては的確で、
その後さらに裏付けが増えている。CLI が選ばれる理由を整理する。
理由 内容 自動化できる GUI は人が押すしかないが、CLI はスクリプトから叩ける(CI/CD の前提) 移植しやすい 画面の描画系を持たないため、Windows / Linux / macOS で同じ実装が動く 合成できる パイプで繋げる。小さな道具を組み合わせるという UNIX の発想 リモートで使える SSH 越しに使える。コンテナの中でも使える 差分が取れる 入出力がテキストなので、diff / grep / ログが効く LLM と相性が良い AI が「叩ける」インターフェイス。GUI は自動操作が難しい 最後の点が、原文の 3 番目の理由をさらに押し進めたものである。
エージェント型の AI ツールが普及した結果、
「AI に操作させられるか」がインターフェイス設計の評価軸に入った。
GUI しかない製品は、この観点で不利になる。なお、CUI と CLI は厳密には別である。
用語 意味 CUI(Character User Interface) 文字ベースの UI 全般(対話的な TUI も含む) CLI(Command Line Interface) コマンドと引数で操作するインターフェイス TUI(Text User Interface) 端末上に画面を描く( vim、htop等)原文が「CUI(CLI)」と併記しているのは、
両方の性格を持つツールを想定しているためと読める
(後述の Sharprompt は対話的= TUI 寄り、
System.CommandLine は引数解析= CLI そのもの)。
何気に、何個か開発&リリースされている。
https://github.com/Cysharp/ConsoleAppFramework
補足(現在の選択肢と使い分け): 3 つが挙げられているが、
役割が違うため、併用するのが実際のところである。
ライブラリ 担当する範囲 System.CommandLine 引数・オプションの解析、ヘルプ生成、補完スクリプト生成 Sharprompt 対話的な入力(選択、確認、パスワード、複数選択) ConsoleAppFramework メソッドをそのままコマンドにする(ホスティング・DI 統合) Spectre.Console 描画(表、ツリー、プログレス、色)+引数解析 CommandLineParser 引数解析(歴史が長い。新規なら上記を優先) 【典型的な組み合わせ】 引数解析 → System.CommandLine(または ConsoleAppFramework) 対話的な入力 → Sharprompt 表示(表・進捗) → Spectre.Console
System.CommandLineの現況に注意する。
長らくプレビュー(beta)のままで、
API が破壊的に変わってきた経緯がある
(2.0 系で大きく整理された)。
バージョンを固定して使うのが無難である。
ConsoleAppFrameworkの v5 は
Source Generator による実装に変わり、
リフレクションを使わないため AOT・起動速度で有利である。// ConsoleAppFramework v5:メソッドがそのままコマンドになる var app = ConsoleApp.Create(); app.Add("hello", (string name, int count = 1) => { for (var i = 0; i < count; i++) Console.WriteLine($"Hello, {name}!"); }); app.Run(args);
- Azure CLI が参考になる。
- テンプレートを作成した。
補足(CLI のログインの定石): Azure CLI を参考にする
という原文の指摘は妥当で、ブラウザに認証を委ねるのが要点である。【CLI のログイン(OAuth 2.0 / OIDC)】 ① CLI が既定のブラウザを開く ↓ ② ユーザーはブラウザ上で認証(MFA・SSO もここで完結) ↓ ③ 認可コードが CLI に返る ・ローカルに立てた HTTP リスナーへリダイレクト(Loopback) ・またはユーザーがコードを貼り付け(Device Code Flow) ↓ ④ CLI がトークンを取得し、安全な場所に保存
方式 用途 Authorization Code + PKCE(Loopback) ブラウザが開ける環境(開発者の手元) Device Code Flow ブラウザが開けない環境(SSH 越し、コンテナ内、CI) クライアント資格情報 人が介在しない(CI/CD、バッチ) CLI に ID とパスワードを直接入力させてはならない。
MFA・条件付きアクセス・SSO が効かなくなり、
かつパスワードがシェル履歴やプロセス一覧に残る。トークンの保存先も設計項目である。
OS 保存先 Windows DPAPI( ProtectedData)で暗号化してファイルにmacOS キーチェーン Linux libsecret(GNOME Keyring 等)、なければ暗号化ファイル 平文の JSON をホーム ディレクトリに置く実装は少なくないが、
最低限ファイル パーミッションを絞る(600相当)。
以下のように実装できる。
- 参考
- コンソール・アプリケーションで進行状況を表示するには?[2.0のみ、C#、VB] - @IT
https://www.atmarkit.co.jp/fdotnet/dotnettips/576consoleprogress/consoleprogress.html - くりーむわーかー : C#のコンソールアプリでカーソルを先頭に戻す
https://cream-worker.blog.jp/archives/1051110023.html
- コンソール・アプリケーションで進行状況を表示するには?[2.0のみ、C#、VB] - @IT
補足(進捗表示の注意点):
Console.SetCursorPositionで
自力で描く方法は今も動くが、落とし穴がある。・出力がリダイレクトされている場合(> file、| grep) → カーソル操作が意味を成さない。制御文字がファイルに混入する ・CI のログ → 端末ではないため、\r による上書きが効かず、行が大量に増える ・幅の狭い端末 → 折り返して表示が崩れる対策:
// 端末かどうかを判定してから描く if (!Console.IsOutputRedirected) { Console.Write($"\r処理中... {percent,3}%"); } else { // ログ向けには、区切りの良い所で 1 行ずつ出す if (percent % 10 == 0) Console.WriteLine($"処理中... {percent}%"); }
Spectre.Consoleを使えばこの辺りは吸収される
(リダイレクト時は自動的に静かな出力に切り替わる)。await AnsiConsole.Progress().StartAsync(async ctx => { var task = ctx.AddTask("[green]処理中[/]", maxValue: total); while (!task.IsFinished) { /* ... */ task.Increment(1); } });
以下のコマンドで短縮可能。
PROMPT $N$G- 参考
- コマンドプロンプトの作業フォルダパスが長すぎるとき - カヤの内
https://kaya574.hatenadiary.org/entry/20110427/1303866987
- コマンドプロンプトの作業フォルダパスが長すぎるとき - カヤの内
以下のコマンドで CMD の出力をクリア可能。
cls補足(PowerShell での対応): 原文の 2 つは CMD の話である。
PowerShell では以下になる。
やりたいこと CMD PowerShell プロンプトを短くする PROMPT $N$Gfunction prompt { "PS> " }画面をクリア clsClear-Host(別名cls、clear)# プロファイルに書けば恒久化される function prompt { "$(Split-Path -Leaf $PWD)> " }Windows Terminal を使っているなら、
oh-my-posh 等でプロンプトを整えるのが現在の主流である。
補足(CLI を自作する際の設計指針): 原文の各論を補う形で、
作法をまとめておく。① 終了コードを正しく返す
return success ? 0 : 1; // 0 = 成功、非 0 = 失敗スクリプトから使われる以上、終了コードが唯一の機械可読な結果である。
ここを常に0にしていると、CI で失敗を検知できない。② 標準出力と標準エラーを分ける
標準出力(stdout)… 処理の「結果」。パイプで次に渡すもの 標準エラー(stderr)… 進捗・警告・エラー。人間向けのメッセージ進捗表示を stdout に出すと、パイプした先が壊れる。
③ 機械可読な出力形式を用意する
mytool list → 人間向けの整形された表 mytool list --output json → JSON(スクリプトから使う)Azure CLI の
--output json/--queryが良い例である。④ 対話を必須にしない
対話的な入力(Sharprompt 等)は便利だが、
必ず「引数で全部指定できる」道を残す。
でないと CI やバッチから使えない。
--yes/--non-interactiveのようなフラグを用意する。⑤ 色と装飾は環境を見て切る
・出力がリダイレクトされている → 色を出さない ・環境変数 NO_COLOR が設定されている → 色を出さない⑥ 配布形態を決める
方式 内容 .NET グローバル ツール dotnet tool install -gで入る。.NET SDK が要る単一ファイル発行 PublishSingleFile。ランタイム不要(自己完結)Native AOT 起動が速く小さい。CLI に最適だが制約あり CLI は起動速度が体感を支配するため、
Native AOT の効果が大きい(数十 ms 対数百 ms)。<PropertyGroup> <PublishAot>true</PublishAot> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup>ただし
InvariantGlobalizationは
カルチャ依存の処理を壊すため、
日本語を扱うなら有効化しない。
https://github.com/OpenTouryoProject/SampleProgram/tree/master/Other/PipesFamilyHouse
-
SPAのIDEのRADサポートはないケド、ツールキットは出ているらしい。
https://www.osscons.jp/jo97gd2il-537/ -
CUI系のツールの時代が来ているのかも知れない(と言うか来ている)。
https://www.osscons.jp/jo4sxysx7-537/ -
第20回 部会 新テンプレ、CLIテンプレートの紹介動画
(Sharprompt、System.CommandLine を使用しています)
https://www.osscons.jp/jodqpha64-537/
- .NET ツールの概要(グローバル ツール)
https://learn.microsoft.com/ja-jp/dotnet/core/tools/global-tools - Native AOT の配置
https://learn.microsoft.com/ja-jp/dotnet/core/deploying/native-aot/ - System.CommandLine の概要
https://learn.microsoft.com/ja-jp/dotnet/standard/commandline/
Tags: 移行, シェル, インフラストラクチャ, Windows, .NET開発