やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

C#でコマンド引数解析する

 C#アトリビュートを使って設定する。

成果物

情報源

 公式ライブラリにコマンド引数パーサは存在しない。そこで、NuGetを使って第三者が作成したライブラリを拝借する。いくつかあるが、最も簡単らしいCommandLineParserを使う。

インストール

 CommandLineParserをインストールする。mono, nuget.exeがインストール済みであること。install.sh

mono $(which nuget.exe) install CommandLineParser

コード

Options.cs

class Options
{
    [Option('a', "aaa", Required = false, HelpText = "ヘルプ用説明文。")]
    public string A { get; set; } = "";
}

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        Parser.Default.ParseArguments<Options>(args)
            .WithParsed(opt => { Run(opt); })
            .WithNotParsed(er => { Error(er); });
    }
    private static void Run(Options opt)
    {
        Console.WriteLine("CommandLine parse success !!");
        Console.WriteLine($"A: {opt.A}");
    }
    private static void Error(IEnumerable<Error> er)
    {
        Console.WriteLine("CommandLine parse failed...");
        foreach (Error e in er)
        {
            Console.WriteLine(e);
        }
    }
}

ビルド

csc \
  -nologo \
  -recurse:*.cs \
  -nullable:enable \
  -langversion:latest \
  -r:CommandLine.dll

実行

mono Program.exe -a ABC
mono Program.exe --aaa ABC
CommandLine parse success !!
A: ABC

所感

 まあまあの使用感。

 サブコマンドも1階層だけなら作れるらしい。でも、ジェネリクスが大変なことになる……。サブコマンドの数だけ増えていく。これはひどい

Parser.Default.ParseArguments<MySubCommand1, MySubCommand2, MySubCommand3>(args)
    .WithParsed<MySubCommand1>(opt1 => { /**/ })
    .WithParsed<MySubCommand2>(opt2 => { /**/ })
    .WithParsed<MySubCommand3>(opt2 => { /**/ })
    .WithNotParsed(er => { /**/ })
    ;

 あと、サブコマンドを省略したときはどうなるの? パース失敗じゃなくサブコマンド1を実行させたい。できるかな? 要検証。

対象環境

$ uname -a
Linux raspberrypi 5.4.72-v7l+ #1356 SMP Thu Oct 22 13:57:51 BST 2020 armv7l GNU/Linux