使用C#中的子命令解析命令行

是否有一个C#的命令行解析库,对git,svn等风格的“子命令”有很好的支持?例如,“git”命令有几个子命令:

git add
git status
git diff
...

必须在子命令名称之前的两个全局选项,以及必须遵循其名称的子命令的特定选项.例如,这些做了不同的事情:

git -p add
git add -p

不同的子命令可能各自具有完全不同的选项和参数集.

我一直在使用NDesk.Options,但到目前为止我还不需要实现子命令.我认为它足够灵活,可以在上面构建子命令,但是如何以简洁优雅的方式做到这一点并不是完全明显的.是否可以在NDesk.Options中执行此操作,或者是否有更合适的C#命令行解析库?

最佳答案 我偏爱
my own option parsing library,我是
blogging about currently.我的确计划涵盖子命令,但是在我达到它之前还需要一段时间(它将是最后的帖子之一).

Nito.KitchenSink.OptionParsing不直接支持子命令,但您可以使用该库仅解析部分命令行,并自行处理子命令. “全局”和“子命令特定”选项集添加了一个有趣的转折,但它可以这样做:

using System;
using System.Linq;
using Nito.KitchenSink.OptionParsing;

class Program
{
  private sealed class GlobalOptions : OptionArgumentsBase
  {
    // Use a better name than "POption". This is just an example.
    [Option('p', OptionArgument.None)]
    public bool POption { get; set; }

    // Override Validate to allow AdditionalArguments.
    public override void Validate()
    {
    }
  }

  private sealed class AddOptions : OptionArgumentsBase
  {
    [Option('p', OptionArgument.None)]
    public bool POption { get; set; }
  }

  static int Main()
  {
    try
    {
      // Parse the entire command line into a GlobalOptions object.
      var options = OptionParser.Parse<GlobalOptions>();

      // The first entry in AdditionalArguments is our sub-command.
      if (options.AdditionalArguments.Count == 0)
        throw new OptionParsingException("No sub-command specified.");
      object subcommandOptions = null;
      string subcommand = options.AdditionalArguments[0];
      switch (subcommand)
      {
        case "add":
        {
          // Parse the remaining arguments as command-specific options.
          subcommandOptions = OptionParser.Parse<AddOptions>(options.AdditionalArguments.Skip(1));
          break;
        }
        case "status": // TODO: Parse command-specific options for this, too.
          break;
        case "diff": // TODO: Parse command-specific options for this, too.
          break;
        default:
          throw new OptionParsingException("Unknown sub-command: " + subcommand);
      }


      // At this point, we have our global options, subcommand, and subcommand options.
      Console.WriteLine("Global -p option: " + options.POption);
      Console.WriteLine("Subcommand: " + subcommand);
      var addOptions = subcommandOptions as AddOptions;
      if (addOptions != null)
        Console.WriteLine("Add-specific -p option: " + addOptions.POption);

      return 0;
    }
    catch (OptionParsingException ex)
    {
      Console.Error.WriteLine(ex.Message);
      // TODO: write out usage information.
      return 2;
    }
    catch (Exception ex)
    {
      Console.Error.WriteLine(ex);
      return 1;
    }
  }
}

上面的示例程序产生以下输出:

>CommandLineParsingTest.exe
No sub-command specified.

>CommandLineParsingTest.exe -p
No sub-command specified.

>CommandLineParsingTest.exe test
Unknown sub-command: test

>CommandLineParsingTest.exe add
Global -p option: False
Subcommand: add
Add-specific -p option: False

>CommandLineParsingTest.exe -p add
Global -p option: True
Subcommand: add
Add-specific -p option: False

>CommandLineParsingTest.exe add -p
Global -p option: False
Subcommand: add
Add-specific -p option: True

>CommandLineParsingTest.exe -p add -p
Global -p option: True
Subcommand: add
Add-specific -p option: True

>CommandLineParsingTest.exe status
Global -p option: False
Subcommand: status

>CommandLineParsingTest.exe -p status
Global -p option: True
Subcommand: status
点赞