在C#中解析命令行参数的最佳方法?[关闭]


731

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

过去,我只是索引/循环该数组,并做了一些正则表达式来提取值。但是,当命令变得更复杂时,解析可能会变得很丑陋。

所以我对以下内容感兴趣:

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

假定命令始终遵循通用标准,例如此处回答


前面的讨论,将包含命令行参数的字符串拆分为c#中的字符串,可能会有一些答案。
吉梅尔

1
嗨,很抱歉,这有点题外话。但是,我确实使用“应用程序设置”将参数传递给应用程序。我发现它非常易于使用,不需要编写参数/文件解析,也不需要额外的库。 msdn.microsoft.com/zh-cn/library/aa730869(VS.80).aspx
叫我史蒂夫(Steve)2010年

44
@call me Steve:命令行参数的重点是每次调用时它们可能会有所不同-您如何使用应用程序设置来做到这一点?
reinierpost

Answers:


324

我强烈建议使用NDesk.Options文档)和/或Mono.Options(相同的API,不同的名称空间)。文档中示例

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;
}

14
NDesk.options很棒,但是似乎并不能很好地支持具有多个不同命令的控制台应用程序。如果您愿意,请尝试基于NDesk.Options构建的ManyConsole:nuget.org/List/Packages/ManyConsole
Frank Schwieterman 2011年

5
当我的一个应用程序带有多个不同的命令时,我会“分层” OptionSet。以mdoc(docs.go-mono.com/index.aspx?link=man%3amdoc%281%29),该文件具有“全局”选项集(github.com/mono/mono/blob/master/mcs/tools/ MDOC / ...),其代表为每个命令OptionSet(例如github.com/mono/mono/blob/master/mcs/tools/mdoc/...
jonp

3
NDesk对我没有工作。可以读取整数参数,但不能读取字符串。变量不断获取参数(例如's','a'等)而不是参数值(例如'serverName','ApplicationName')。放弃并使用“命令行解析器库”。好的,到目前为止。
杰伊

2
@AshleyHenderson一方面,它小巧而灵活。大多数解决方案只能使用可选的命名参数(例如,不能像git checkout master),或者它们的参数不灵活(即,不支持--foo 123= --foo=123= -f 123 = -f=123-v -h= -vh)。
12

1
@FrankSchwieterman应该是自己的答案。并感谢您的提示,ManyConsole是真正的零食,非常适合我。
quentin-starin

197

我真的很喜欢命令行解析器库(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();
    }
}

6
这也是我所选择的图书馆。我正在为一家需要维护多年的大型公司编写应用程序-该库自2005年以来一直在不断更新,它似乎很流行,由活跃于C#社区的人们编写,并以BSD风格获得许可支持确实消失了。
查尔斯·伯恩斯

我也推荐这个。我唯一的问题是:指定允许的辩论组合(例如,如果进行移动辩论必须同时具有源和目标论点)可能与属性有关。但是您最好使用单独的Argument验证器逻辑来做
Lyndon White

1
我喜欢Options类。似乎也支持未命名的参数和标志--recursive
2012年

2
我刚刚对其进行了测试,并且在短短几分钟内就为我的应用程序实现了选项。使用库非常简单。
Trismegistos 2012年

3
我发现这个库对我自己来说非常严格。如果需要专用集,则不能为每个集定义必需的选项,因此必须手动检查它们。您不能为未命名的值定义最小要求,还必须手动检查它们。帮助屏幕生成器也不是很灵活。如果该库的行为无法立即满足您的需求,那么您几乎无能为力地对其进行更改。
谢尔盖·科斯特鲁科夫

50

WPF TestApi库配备了最好的命令行解析器C#开发的一个。我强烈建议您从Ivo Manolov的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);

19
+1。命令行解析是真正应该来自供应商(即Microsoft)而不是通过第三方工具进行的解析,即使供应商的支持是绕来绕去的。
Joel Coehoorn

2
就是说,被接受的答案(单声道)是下一个最好的选择。
Joel Coehoorn

6
@Joel,命令行解析必须来自供应商的那一部分重要?你是什​​么原因
greenoldman

3
@marcias:我认为他的意思可能应该是开箱即用……就像很多东西一样:)
user7116 2010年

图书馆很大!包含的内容远远超出了我的需要……
Riri

24

2
NDesk选项有一个非常不错的API
2009年

2
我将对NDesk再投一票,它运作良好,具有非侵入性且有据可查。
泰伦斯

1
Mono.GetOptions很老,NDesk.Options更好(或者Mono.Options,如果您愿意,它是同一个类,在这里:anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options / ...
Matt Enright 2010年

7
@Adam Oren:我的回答是1年1个月大!单声道主干的结构已重构。该代码将立即向anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/...
abatishchev

6
@Tormod:它是过时的Mono.GetOptions,而不是Mono.Options。Mono.Options仍然保持。
jonp 2010年

14

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

http://bizark.codeplex.com/

该库包含一个命令行解析器,它将使用命令行中的值初始化一个类。它具有大量功能(多年来我一直在构建它)。

文档中 ...

BizArk框架中的命令行分析具有以下关键功能:

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

2
我发现BizArk的命令行解析器比其他解析器更轻松,更流畅。强烈推荐!
鲍里斯·莫迪列夫斯基


9

CLAP(命令行参数解析器)具有可用的API,并且文档详尽。您可以制作一个方法,并为参数添加注释。 https://github.com/adrianaisemberg/CLAP


2
它非常简单易用,而且他们的网站令人震惊。但是,它们的语法不是很直观:(myapp myverb -argname argvalue必须-argname)或myapp -help(通常--help)。
2012年

@Wernight,可以在动词上使用IsDefault参数,以将其省略。我没有找到对位置参数的支持,但是我自己在解析命令行时只使用了位置参数。使用命名参数后跟值恕我直言要清楚得多。
Loudenvier 2013年

5

有许多解决此问题的方法。为了完整性和提供替代方案,如果有人希望我在我的谷歌代码库中为两个有用的类添加答案。

第一个是ArgumentList,它仅负责解析命令行参数。它收集由开关“ / x:y”或“ -x = y”定义的名称/值对,并收集“未命名”条目的列表。它的基本用法是这里讨论在这里查看类

第二部分是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处理/ ivalue = 5 -svalue:“字符串值”

就这么简单,或者像您需要的那样复杂。您可以查看源代码查看帮助下载二进制文件


4

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

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


4

你可能喜欢我的 地毯

易于使用且可扩展的命令行参数解析器。句柄:布尔,加号/减号,字符串,字符串列表,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,我将确保我对自己的从属关系更加明确。
Phill Tew 2012年

不用担心...那里有一些类似的东西(我不是其中之一),所以我喜欢提请大家注意。同样,对于开源项目来说通常不是问题。这主要是为了阻止人们向他们的(付费)产品发送垃圾邮件建议。
杰森·唐

3

在以下位置有一个命令行参数解析器 http://www.codeplex.com/commonlibrarynet上

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

它可以处理以下内容:

- 配置:QA - STARTDATE:$ { 今天 } - 区域: '纽约' Settings01

它很容易使用。


2

这是我基于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;
    }
}

2

我个人最喜欢的是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;
}

2

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

  • 它易于使用-尽管我找不到它的文档,但是FubuCore解决方案还提供了一个项目,其中包含一组不错的单元测试,这些单元测试比任何文档都更多地介绍了功能。
  • 它具有出色的面向对象设计,没有代码重复或我以前在命令行解析应用中拥有的其他类似内容
  • 这是声明性的:您基本上是为Commands和参数集编写类,并用属性修饰它们以设置各种选项(例如,名称,描述,强制/可选)
  • 库甚至根据这些定义打印出一个不错的使用图

以下是有关如何使用此功能的简单示例。为了说明这种用法,我编写了一个简单的实用程序,其中包含两个命令:-添加(将对象添加到列表中-对象由名称(字符串),值(int)和布尔值标志组成)-列表(列表所有当前添加的对象)

首先,我为“ 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; }
}

下一个命令是“列表”,其实现方式如下:

[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
  ------------------------

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

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
  -------------------------------------


2

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


有据可查?文档在哪里?
Suhas

有内部文档(即在代码库中)和外部文档(请参见Readme.mkd文件Documentation夹中的文件)。
伯纳德

好吧,我匆匆发表了评论。可能是您可以将项目移至github,并且文档会自动开始出现在主页上。
Suhas 2013年


0

我建议使用开源库CSharpOptParse。它解析命令行,并使用命令行输入合并用户定义的.NET对象。在编写C#控制台应用程序时,我总是转向该库。



0

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

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"));
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.