OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

.NET コマンドライン引数の解析とUsage作成を行う「CuiCommandParser」を作成

C#/.NET のコンソールアプリで、実行コマンドに渡された引数の解析と、helpオプションで表示するヘルプメッセージ(Usage)用の文字列生成を行う機能をあわせてライブラリ化しました。

CuiCommandParser(NuGet)

ソースはこちら

READMEにだいたい書いたんだけども、一応こちらにも日本語で。

何をするもの?

一般的なUnixスタイルのコマンド引数の解析をするのと、Usageのメッセージをそれっぽい書式で作るもの。

  • -h とか --hoge とかの形でオプションを指定する想定
  • -xyz のようにオプションをまとめて指定することもできる
  • オプションとその値とは = または半角スペースでつなぐ

使い方

主なクラスは以下2つ

CommandParser

コマンド解析クラス。

オプション情報を RegisterOption で登録してから Parse を実行する。

$ mv -S=_bu note.txt child_dir/ という感じで実行されたmvコマンドの解析例を以下に。

static void Main(string[] args)
{
    var parser = new CommandParser();
    var options = new[]
    {
        new CommandOption(null, "backup", "CONTROL",
                          "削除されるファイルのバックアップを作成する",
                          CommandOptionKind.HasOptionalValue),
        new CommandOption('b', null, null,
                          "backupオプションと同じだが、引数をとれない",
                          CommandOptionKind.NoValue),
        new CommandOption('f', "force", null,
                          "確認をせずに上書きをする",
                          CommandOptionKind.NoValue),
        new CommandOption('S', "suffix", "SUFFIX",
                          "バックアップの形式を決める",
                          CommandOptionKind.NeedsValue),
        //...
    };

    foreach (var opt in options)
    {
        parser.RegisterOption(opt);
    }

    var parsed = parser.Parse(args);

    if (parsed == null)
    {
        //オプションの指定が不正だった場合
        return;
    }
    
    //オプション以外のパラメータ
    var parameters = parsed.CommandParameters;   // -> [ "note.txt", "child_dir/" ]

    //オプションの有無を確認(以下全て同じ)
    var forced = parsed.HasOption('f');          // -> false
    // var forced = parsed.HasOption("force");
    // var forced = parsed.HasOption(options[2]);

    //オプションの値を取得(以下全て同じ)
    var suffix = parsed.GetOptionValue('S');     // -> "_bu"
    // var suffix = parsed.GetOptionValue("suffix");
    // var suffix = parsed.GetOptionValue(options[3]);
}

CommandUsageBuilder

Usage生成クラス。

CommandParserRegisterOption してから NewUsageBuilder を呼べば、登録したオプション情報を引き継いでビルダインスタンスを生成できる。

同じく mv コマンドの例。

var builder = parser.NewUsageBuilder("mv");
builder.OptionKeyValueSeparator = '=';
builder.Summary = "ファイル名の変更、もしくは複数のファイルをディレクトリへ移動します。";

builder.AddUseCase(builder.NewUseCase().AddArg(builder.NewUseCaseArg("OPTION").AsMultiple().AsOptional())
                                       .AddArg(builder.NewUseCaseArg("-T").AsOptional())
                                       .AddArg(builder.NewUseCaseArg("SOURCE"))
                                       .AddArg(builder.NewUseCaseArg("DEST")));

builder.AddUseCase(builder.NewUseCase().AddArg(builder.NewUseCaseArg("OPTION").AsMultiple().AsOptional())
                                       .AddArg(builder.NewUseCaseArg("SOURCE").AsMultiple())
                                       .AddArg(builder.NewUseCaseArg("DIRECTORY")));

builder.AddUseCase(builder.NewUseCase().AddArg(builder.NewUseCaseArg("OPTION").AsMultiple().AsOptional())
                                       .AddArg(builder.NewUseCaseArg("-t").Value("DIRECTORY"))
                                       .AddArg(builder.NewUseCaseArg("SOURCE").AsMultiple()));

Console.WriteLine(builder.ToString());

出力結果

ファイル名の変更、もしくは複数のファイルをディレクトリへ移動します。

Usage: mv [OPTION]... [-T] SOURCE DEST
   or: mv [OPTION]... SOURCE... DIRECTORY
   or: mv [OPTION]... -t=DIRECTORY SOURCE...

Arguments:
      --backup[=CONTROL]  削除されるファイルのバックアップを作成する
  -b                      backupオプションと同じだが、引数をとれない
  -f, --force             確認をせずに上書きをする
  -S, --suffix=SUFFIX     バックアップの形式を決める

以上。

NuGetで検索すると似たような名前のライブラリがけっこうあったので、たぶん同じようなことできるライブラリもあるんだろうけど、期待通りの動きするものを探すのもけっこう手間で、このくらいのものなら作ってしまったほうが早かったりしますね。