【问题标题】:Best way to parse command line arguments in C#? [closed]在 C# 中解析命令行参数的最佳方法是什么? [关闭]
【发布时间】:2013-06-17 09:57:33
【问题描述】:

在构建带参数的控制台应用程序时,您可以使用传递给Main(string[] args) 的参数。

在过去,我只是简单地索引/循环该数组并使用一些正则表达式来提取值。但是,当命令变得更复杂时,解析会变得非常难看。

所以我对:

  • 您使用的库
  • 您使用的模式

假设命令始终遵守通用标准,例如 answered here

【问题讨论】:

标签: c# .net command-line-arguments


【解决方案1】:

我强烈建议使用NDesk.Options (Documentation) 和/或Mono.Options(相同的API,不同的命名空间)。一个example from the documentation

bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;

var p = new OptionSet () {
    { "n|name=", "the {NAME} of someone to greet.",
       v => names.Add (v) },
    { "r|repeat=", 
       "the number of {TIMES} to repeat the greeting.\n" + 
          "this must be an integer.",
        (int v) => repeat = v },
    { "v", "increase debug message verbosity",
       v => { if (v != null) ++verbosity; } },
    { "h|help",  "show this message and exit", 
       v => show_help = v != null },
};

List<string> extra;
try {
    extra = p.Parse (args);
}
catch (OptionException e) {
    Console.Write ("greet: ");
    Console.WriteLine (e.Message);
    Console.WriteLine ("Try `greet --help' for more information.");
    return;
}

【讨论】:

  • NDesk.options 很棒,但似乎并不能真正支持具有多个不同命令的控制台应用程序。如果你想这样做,请尝试基于 NDesk.Options 构建的 ManyConsole:nuget.org/List/Packages/ManyConsole
  • 当我有一个包含多个不同命令的应用程序时,我会“分层”选项集。以 mdoc (docs.go-mono.com/index.aspx?link=man%3amdoc%281%29) 为例,它有一个“全局”选项集 (github.com/mono/mono/blob/master/mcs/tools/mdoc/…),它委托给每个命令的选项集(例如 github.com/mono/mono/blob/master/mcs/tools/mdoc/…
  • NDesk 不适合我。可以读取整数参数,但不能读取字符串。变量不断获取参数(例如's'、'a'等)而不是参数值(例如'serverName'、'ApplicationName')。放弃并改用“命令行解析器库”。到目前为止还好。
  • @AshleyHenderson 一方面,它小巧灵活。大多数解决方案仅适用于可选的命名参数(即,不能像 git checkout master 那样做),或者它们的参数不灵活(即,不支持 --foo 123 = --foo=123 = -f 123= -f=123 和 @ 987654335@ = -vh)。
  • @FrankSchwieterman 这应该是它自己的答案。感谢您的提示,ManyConsole 是一种真正的享受,非常适合我。
【解决方案2】:

我真的很喜欢命令行解析器库 (http://commandline.codeplex.com/)。它有一种非常简单优雅的通过属性设置参数的方式:

class Options
{
    [Option("i", "input", Required = true, HelpText = "Input file to read.")]
    public string InputFile { get; set; }

    [Option(null, "length", HelpText = "The maximum number of bytes to process.")]
    public int MaximumLenght { get; set; }

    [Option("v", null, HelpText = "Print details during execution.")]
    public bool Verbose { get; set; }

    [HelpOption(HelpText = "Display this help screen.")]
    public string GetUsage()
    {
        var usage = new StringBuilder();
        usage.AppendLine("Quickstart Application 1.0");
        usage.AppendLine("Read user manual for usage instructions...");
        return usage.ToString();
    }
}

【讨论】:

  • 这也是我选择的图书馆。我正在为一家需要维护多年的大公司编写应用程序——这个库自 2005 年以来一直在不断更新,似乎很受欢迎,由活跃于 C# 社区的人编写,并以 BSD 风格获得许可,以防万一支持确实消失了。
  • 我也推荐这个。我唯一的问题是:指定允许的争论组合(例如,如果有移动争论也必须有源和目标参数)可能可能与属性有关。但是你最好使用单独的参数验证器逻辑来做这件事
  • 我喜欢 Options 类。它似乎也支持像--recursive 这样的未命名参数和标志。
  • 我刚刚对其进行了测试,并在几分钟内为我的应用程序实现了选项。使用库非常简单。
  • 我发现这个库对自己来说非常严格。如果您需要独占集,则无法为每个集定义所需的选项,因此必须手动检查它们。您不能为未命名值定义最低要求,您还必须手动检查它们。帮助屏幕生成器也根本不灵活。如果库的行为不能满足您开箱即用的需求,您几乎无法更改它。
【解决方案3】:

WPF TestApi library 带有用于 C# 开发的最佳命令行解析器之一。我强烈建议您查看它,来自Ivo Manolov's blog on the API

// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
   bool? Verbose { get; set; }
   int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);

【讨论】:

  • +1。命令行解析确实应该来自供应商(即微软),而不是通过第三方工具,即使供应商的支持是以迂回的方式提供的。
  • 也就是说,接受的答案(单声道)是次优的。
  • @Joel,命令行解析必须来自供应商的重要部分是什么?你的理由是什么?
  • @marcias:我认为他的意思是它可能应该是开箱即用的......就像很多事情一样 :)
  • 图书馆很大!包含的内容比我需要的要多...
【解决方案4】:

【讨论】:

【解决方案5】:

看起来每个人都有自己的宠物命令行解析器,我最好也添加我的:)。

http://bizark.codeplex.com/

这个库包含一个command-line parser,它将使用命令行中的值初始化一个类。它有很多功能(我多年来一直在构建它)。

来自documentation...

BizArk 框架中的命令行解析具有以下关键特性:

  • 自动初始化:根据命令行参数自动设置类属性。
  • 默认属性:在不指定属性名称的情况下发送值。
  • 值转换:使用 BizArk 中也包含的强大的 ConvertEx 类将值转换为正确的类型。
  • 布尔标志:标志可以通过简单地使用参数来指定(例如,/b 代表真,/b- 代表假)或通过添加值 true/false、yes/no 等来指定。李>
  • 参数数组:只需在命令行名称后添加多个值即可设置定义为数组的属性。例如,/x 1 2 3 将使用数组 { 1, 2, 3 } 填充 x(假设 x 被定义为整数数组)。
  • 命令行别名:一个属性可以支持多个命令行别名。例如,Help 使用别名 ?。
  • 部分名称识别:您无需拼出全名或别名,只需拼写足以让解析器区分属性/别名与其他名称。
  • 支持 ClickOnce: 可以初始化属性,即使它们被指定为 ClickOnce 部署应用程序的 URL 中的查询字符串。命令行初始化方法会检测它是否作为 ClickOnce 运行,因此您的代码在使用时无需更改。
  • 自动创建 /?帮助: 这包括考虑到控制台宽度的良好格式。
  • 将命令行参数加载/保存到文件:如果您有多个大型、复杂的命令行参数集并且要多次运行,这将特别有用。

【讨论】:

  • 我发现 BizArk 的命令行解析器比其他的更容易和流畅。强烈推荐!
【解决方案6】:

不久前我写了一个 C# 命令行参数解析器。它的地址是:http://www.codeplex.com/CommandLineArguments

【讨论】:

    【解决方案7】:

    CLAP(命令行参数解析器)有一个可用的 API,并且有很好的文档记录。您创建一个方法,注释参数。 https://github.com/adrianaisemberg/CLAP

    【讨论】:

    • 它使用起来非常简单,而且他们的网站很震撼。但是它们的语法不是很直观:myapp myverb -argname argvalue(必须有-argname)或myapp -help(通常是--help)。
    • @Wernight 您可以在动词上使用 IsDefault 参数,这样做可以省略。我没有找到对位置参数的支持,但是我自己解析命令行时确实只使用了位置参数。恕我直言,使用命名参数后跟值要清楚得多。
    【解决方案8】:

    这个问题有很多解决方案。为了完整性并提供替代方案,如果有人希望我在我的google code library 中为两个有用的类添加这个答案。

    第一个是ArgumentList,它只负责解析命令行参数。它收集由开关“/x:y”或“-x=y”定义的名称-值对,还收集“未命名”条目的列表。这是基本的usage is discussed hereview the class here

    第二部分是CommandInterpreter,它从您的.Net 类中创建一个功能齐全的命令行应用程序。举个例子:

    using CSharpTest.Net.Commands;
    static class Program
    {
        static void Main(string[] args)
        {
            new CommandInterpreter(new Commands()).Run(args);
        }
        //example ‘Commands’ class:
        class Commands
        {
            public int SomeValue { get; set; }
            public void DoSomething(string svalue, int ivalue)
            { ... }
    

    使用上面的示例代码,您可以运行以下命令:

    Program.exe DoSomething "字符串值" 5

    -- 或--

    Program.exe dosomething /ivalue=5 -svalue:"string value"

    它可以简单到你需要的那样复杂。您可以review the source codeview the helpdownload the binary

    【讨论】:

      【解决方案9】:

      我喜欢that one,因为您可以为参数“定义规则”,无论是否需要,...

      或者,如果您是 Unix 用户,那么您可能会喜欢 GNU Getopt .NET 端口。

      【讨论】:

        【解决方案10】:

        你可能会喜欢我的Rug.Cmd

        易于使用且可扩展的命令行参数解析器。句柄:布尔、加/减、字符串、字符串列表、CSV、枚举。

        内置“/?”帮助模式。

        内置“/??”和 '/?D' 文档生成器模式。

        static void Main(string[] args) 
        {            
            // create the argument parser
            ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");
        
            // create the argument for a string
            StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");
        
            // add the argument to the parser 
            parser.Add("/", "String", StringArg);
        
            // parse arguemnts
            parser.Parse(args);
        
            // did the parser detect a /? argument 
            if (parser.HelpMode == false) 
            {
                // was the string argument defined 
                if (StringArg.Defined == true)
                {
                    // write its value
                    RC.WriteLine("String argument was defined");
                    RC.WriteLine(StringArg.Value);
                }
            }
        }
        

        编辑:这是我的项目,因此不应将这个答案视为第三方的认可。也就是说,我确实将它用于我编写的每个基于命令行的程序,它是开源的,我希望其他人可以从中受益。

        【讨论】:

        • 仅供参考,您应该声明您隶属于 Rug.Cmd 项目(如常见问题解答中所述):stackoverflow.com/faq#promotion -- 没什么大不了的,因为您是推广一个开源项目,但添加免责声明仍然很好;)顺便说一下+1...看起来做得很好。
        • 为指出这一点而欢呼,并感谢您的 +1,我会确保我更明确地说明我的隶属关系。
        • 不用担心......这种类型的事情有一些坚持者(我不是其中之一),所以我喜欢提醒人们。同样,对于开源项目来说,这通常不是问题。这主要是为了阻止人们向他们的(付费)产品发送垃圾邮件推荐。
        【解决方案11】:

        http://www.codeplex.com/commonlibrarynet 有一个命令行参数解析器

        它可以使用
        解析参数 1. 属性
        2. 显式调用
        3. 单行多参数或字符串数​​组

        它可以处理以下事情:

        -config:Qa -startdate:${today} -region:'New York' Settings01

        它非常易于使用。

        【讨论】:

          【解决方案12】:

          这是我基于 Novell Options 类编写的处理程序。

          这是针对执行while (input !="exit") 样式循环的控制台应用程序,例如FTP 控制台等交互式控制台。

          示例用法:

          static void Main(string[] args)
          {
              // Setup
              CommandHandler handler = new CommandHandler();
              CommandOptions options = new CommandOptions();
          
              // Add some commands. Use the v syntax for passing arguments
              options.Add("show", handler.Show)
                  .Add("connect", v => handler.Connect(v))
                  .Add("dir", handler.Dir);
          
              // Read lines
              System.Console.Write(">");
              string input = System.Console.ReadLine();
          
              while (input != "quit" && input != "exit")
              {
                  if (input == "cls" || input == "clear")
                  {
                      System.Console.Clear();
                  }
                  else
                  {
                      if (!string.IsNullOrEmpty(input))
                      {
                          if (options.Parse(input))
                          {
                              System.Console.WriteLine(handler.OutputMessage);
                          }
                          else
                          {
                              System.Console.WriteLine("I didn't understand that command");
                          }
          
                      }
          
                  }
          
                  System.Console.Write(">");
                  input = System.Console.ReadLine();
              }
          }
          

          来源:

          /// <summary>
          /// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
          /// </summary>
          public class CommandOptions
          {
              private Dictionary<string, Action<string[]>> _actions;
              private Dictionary<string, Action> _actionsNoParams;
          
              /// <summary>
              /// Initializes a new instance of the <see cref="CommandOptions"/> class.
              /// </summary>
              public CommandOptions()
              {
                  _actions = new Dictionary<string, Action<string[]>>();
                  _actionsNoParams = new Dictionary<string, Action>();
              }
          
              /// <summary>
              /// Adds a command option and an action to perform when the command is found.
              /// </summary>
              /// <param name="name">The name of the command.</param>
              /// <param name="action">An action delegate</param>
              /// <returns>The current CommandOptions instance.</returns>
              public CommandOptions Add(string name, Action action)
              {
                  _actionsNoParams.Add(name, action);
                  return this;
              }
          
              /// <summary>
              /// Adds a command option and an action (with parameter) to perform when the command is found.
              /// </summary>
              /// <param name="name">The name of the command.</param>
              /// <param name="action">An action delegate that has one parameter - string[] args.</param>
              /// <returns>The current CommandOptions instance.</returns>
              public CommandOptions Add(string name, Action<string[]> action)
              {
                  _actions.Add(name, action);
                  return this;
              }
          
              /// <summary>
              /// Parses the text command and calls any actions associated with the command.
              /// </summary>
              /// <param name="command">The text command, e.g "show databases"</param>
              public bool Parse(string command)
              {
                  if (command.IndexOf(" ") == -1)
                  {
                      // No params
                      foreach (string key in _actionsNoParams.Keys)
                      {
                          if (command == key)
                          {
                              _actionsNoParams[key].Invoke();
                              return true;
                          }
                      }
                  }
                  else
                  {
                      // Params
                      foreach (string key in _actions.Keys)
                      {
                          if (command.StartsWith(key) && command.Length > key.Length)
                          {
          
                              string options = command.Substring(key.Length);
                              options = options.Trim();
                              string[] parts = options.Split(' ');
                              _actions[key].Invoke(parts);
                              return true;
                          }
                      }
                  }
          
                  return false;
              }
          }
          

          【讨论】:

            【解决方案13】:

            我个人最喜欢的是 Peter Palotas 的 http://www.codeproject.com/KB/recipes/plossum_commandline.aspx

            [CommandLineManager(ApplicationName="Hello World",
                Copyright="Copyright (c) Peter Palotas")]
            class Options
            {
               [CommandLineOption(Description="Displays this help text")]
               public bool Help = false;
            
               [CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
               public string Name
               {
                  get { return mName; }
                  set
                  {
                     if (String.IsNullOrEmpty(value))
                        throw new InvalidOptionValueException(
                            "The name must not be empty", false);
                     mName = value;
                  }
               }
            
               private string mName;
            }
            

            【讨论】:

              【解决方案14】:

              我最近遇到了我非常喜欢的 FubuCore 命令行解析实现,原因是:

              • 它很容易使用 - 虽然我找不到它的文档,但 FubuCore 解决方案还提供了一个项目,其中包含一组很好的单元测试,这些测试比任何文档都更能说明功能
              • 它有一个很好的面向对象设计,没有代码重复或我以前在命令行解析应用程序中遇到的其他类似情况
              • 它是声明性的:您基本上为命令和参数集编写类,并用属性装饰它们以设置各种选项(例如名称、描述、强制/可选)
              • 该库甚至会根据这些定义打印出漂亮的使用情况图表

              下面是一个关于如何使用它的简单示例。为了说明用法,我编写了一个简单的实用程序,它有两个命令: - 添加(将对象添加到列表中 - 对象由名称(字符串)、值(整数)和布尔标志组成) - list(列出所有当前添加的对象)

              首先,我为'add'命令编写了一个Command类:

              [Usage("add", "Adds an object to the list")]
              [CommandDescription("Add object", Name = "add")]
              public class AddCommand : FubuCommand<CommandInput>
              {
                  public override bool Execute(CommandInput input)
                  {
                      State.Objects.Add(input); // add the new object to an in-memory collection
              
                      return true;
                  }
              }
              

              这个命令接受一个 CommandInput 实例作为参数,所以我定义下一个:

              public class CommandInput
              {
                  [RequiredUsage("add"), Description("The name of the object to add")]
                  public string ObjectName { get; set; }
              
                  [ValidUsage("add")]
                  [Description("The value of the object to add")]
                  public int ObjectValue { get; set; }
              
                  [Description("Multiply the value by -1")]
                  [ValidUsage("add")]
                  [FlagAlias("nv")]
                  public bool NegateValueFlag { get; set; }
              }
              

              下一个命令是'list',实现如下:

              [Usage("list", "List the objects we have so far")]
              [CommandDescription("List objects", Name = "list")]
              public class ListCommand : FubuCommand<NullInput>
              {
                  public override bool Execute(NullInput input)
                  {
                      State.Objects.ForEach(Console.WriteLine);
              
                      return false;
                  }
              }
              

              'list' 命令不带参数,所以我为此定义了一个 NullInput 类:

              public class NullInput { }
              

              现在剩下的就是将其连接到 Main() 方法中,如下所示:

                  static void Main(string[] args)
                  {
                      var factory = new CommandFactory();
                      factory.RegisterCommands(typeof(Program).Assembly);
              
                      var executor = new CommandExecutor(factory);
              
                      executor.Execute(args);
                  }
              

              程序按预期工作,打印有关正确用法的提示,以防任何命令无效:

                ------------------------
                  Available commands:
                ------------------------
                   add -> Add object
                  list -> List objects
                ------------------------
              

              以及“添加”命令的示例用法:

              Usages for 'add' (Add object)
                add <objectname> [-nv]
              
                -------------------------------------------------
                  Arguments
                -------------------------------------------------
                   objectname -> The name of the object to add
                  objectvalue -> The value of the object to add
                -------------------------------------------------
              
                -------------------------------------
                  Flags
                -------------------------------------
                  [-nv] -> Multiply the value by -1
                -------------------------------------
              

              【讨论】:

                【解决方案15】:

                Powershell 命令行开关。

                Powershell 根据命令行开关上指定的属性完成解析,支持验证、参数集、流水线、错误报告、帮助,以及最好的返回 .NET 对象以用于其他命令行开关。

                我发现一些对入门很有帮助的链接:

                【讨论】:

                  【解决方案16】:

                  C# CLI 是我写的一个非常简单的命令行参数解析库。它有据可查且开源。

                  【讨论】:

                  • 有据可查?文档在哪里?
                  • 有内部文档(即在代码库中)以及外部文档(参见Documentation 文件夹中的Readme.mkd 文件)。
                  • 好吧,我仓促评论了。也许您可以将您的项目移动到 github 并且您的文档会自动开始出现在主页上。
                  【解决方案17】:

                  Genghis Command Line Parser 可能有点过时,但它的功能非常完整,对我来说效果很好。

                  【讨论】:

                  【解决方案18】:

                  我建议使用开源库CSharpOptParse。它解析命令行并使用命令行输入来填充用户定义的 .NET 对象。在编写 C# 控制台应用程序时,我总是求助于这个库。

                  【讨论】:

                    【解决方案19】:

                    请使用 apache commons cli API 的 .net 端口。这很好用。

                    http://sourceforge.net/projects/dotnetcli/

                    以及用于概念和介绍的原始 API

                    http://commons.apache.org/cli/

                    【讨论】:

                      【解决方案20】:

                      一个非常简单易用的临时类用于命令行解析,支持默认参数。

                      class CommandLineArgs
                      {
                          public static CommandLineArgs I
                          {
                              get
                              {
                                  return m_instance;
                              }
                          }
                      
                          public  string argAsString( string argName )
                          {
                              if (m_args.ContainsKey(argName)) {
                                  return m_args[argName];
                              }
                              else return "";
                          }
                      
                          public long argAsLong(string argName)
                          {
                              if (m_args.ContainsKey(argName))
                              {
                                  return Convert.ToInt64(m_args[argName]);
                              }
                              else return 0;
                          }
                      
                          public double argAsDouble(string argName)
                          {
                              if (m_args.ContainsKey(argName))
                              {
                                  return Convert.ToDouble(m_args[argName]);
                              }
                              else return 0;
                          }
                      
                          public void parseArgs(string[] args, string defaultArgs )
                          {
                              m_args = new Dictionary<string, string>();
                              parseDefaults(defaultArgs );
                      
                              foreach (string arg in args)
                              {
                                  string[] words = arg.Split('=');
                                  m_args[words[0]] = words[1];
                              }
                          }
                      
                          private void parseDefaults(string defaultArgs )
                          {
                              if ( defaultArgs == "" ) return;
                              string[] args = defaultArgs.Split(';');
                      
                              foreach (string arg in args)
                              {
                                  string[] words = arg.Split('=');
                                  m_args[words[0]] = words[1];
                              }
                          }
                      
                          private Dictionary<string, string> m_args = null;
                          static readonly CommandLineArgs m_instance = new CommandLineArgs();
                      }
                      
                      class Program
                      {
                          static void Main(string[] args)
                          {
                              CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
                              Console.WriteLine("Arg myStringArg  : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
                              Console.WriteLine("Arg someLong     : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
                          }
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 2022-01-21
                        • 2010-10-04
                        • 2013-01-25
                        • 2020-03-14
                        相关资源
                        最近更新 更多