【问题标题】:Parse command-line with sub-commands in C# [closed]在 C# 中使用子命令解析命令行 [关闭]
【发布时间】:2011-09-24 08:18:21
【问题描述】:

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

git add
git status
git diff
...

有两个必须在子命令名称之前的全局选项,以及必须在其名称之后的子命令特定的选项。例如,它们做不同的事情:

git -p add
git add -p

不同的子命令可能都有完全不同的选项和参数集。

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

【问题讨论】:

    标签: c# command-line-arguments


    【解决方案1】:

    我偏爱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
    

    【讨论】:

    • 看起来很有趣。我不确定我是否理解它如何处理存在相同全局和子命令选项的(诚然令人困惑的)情况。不会从 options.AdditionalArguments 中删除“-p”,因此永远无法设置 subcommandOptions.POption 吗?
    • 第一次调用OptionParser.Parse,只考虑GlobalOptions定义的选项。这将消耗子命令之前的所有参数(包括-p)。由于子命令未被识别为有效的全局选项,因此命令行的其余部分仅放置在 GlobalOptions.AdditionalArguments 中(所有剩余选项都被视为位置参数,它们不会被解析)。在评估子命令(手动)之后,我再次调用OptionParser.Parse,这次将剩余的命令行传递给它,只考虑AddOptions 中定义的选项。
    • 随意给它一个测试运行 - 我从一个通过 NuGet 安装了 Nito.KitchenSink.OptionParsing 的工作控制台应用程序中复制/粘贴了上述内容。
    • 啊,我明白了。我没想到第一个位置参数之后的所有内容都会被视为位置参数。现在我知道它是如何工作的了。
    【解决方案2】:

    你可以试试开源项目“命令行”:

    http://commandline.codeplex.com/

    【讨论】:

    • 它是否支持子命令?我没有看到文档中提到的任何类似内容。是否有任何理由在命令行之上构建子命令比 NDesk.Options 更容易?
    • commandline.codeplex.com/… 可能会给你选择。我看到一些包含参数的选项,你可以从那里处理它。
    猜你喜欢
    • 2010-10-26
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多