关于.NET:解析C#中命令行参数的最佳方法?

Best way to parse command line arguments in C#?

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

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

所以我感兴趣的是:

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

假设命令始终遵循常见标准,如此处所回答的标准。


我强烈建议使用ndesk.options(文档)和/或mono.options(相同的API,不同的命名空间)。文档中的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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.
"
+
         "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;
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
    }
}


WPF testapi库附带了用于C开发的最好的命令行解析器之一。我强烈建议从Ivo Manolov在API上的博客中查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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);


查看http://github.com/mono/mono/tree/master/mcs/class/mono.options/


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

http://bizark.codeplex.com/

这个库包含一个命令行分析器,它将用命令行中的值初始化一个类。它有很多功能(我已经建立了很多年)。

从文档中…

Bizark框架中的命令行分析具有以下主要功能:

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


不久前我编写了一个C命令行参数分析器。网址:http://www.codeplex.com/commandlinearguments


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


这个问题有很多解决办法。为了完整性和提供备选方案,如果有人愿意,我在我的谷歌代码库中为两个有用的类添加了这个答案。

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

第二部分是命令解释器,它从.NET类中创建一个功能完整的命令行应用程序。举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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"string value" 5

--或者——

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

它是如此简单,或是你所需要的那么复杂。您可以查看源代码、查看帮助或下载二进制文件。


你可以喜欢我的一块地毯。

易于使用和扩展的命令行参数分析器。句柄:bool、加号/减号、字符串、字符串列表、csv、枚举。

内置的'/'?帮助模式。

内置的?"和"/?d'文档生成器模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
        }
    }
}

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


我喜欢这个,因为你可以为参数"定义规则",无论需要与否,…

或者如果你是一个Unix用户,你可能会喜欢gnu getopt.net端口。


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

它可以使用1。属性2。显式调用三。多个参数或字符串数组的单行

它可以处理以下事情:

-配置:qa-开始日期:$今天-地区:'纽约'设置01

它很容易使用。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[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;
}

C cli是我编写的一个非常简单的命令行参数解析库。它有良好的文档记录和开源。


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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
[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实例作为参数,因此我定义下一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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",其实现如下:

1
2
3
4
5
6
7
8
9
10
11
[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类:

1
public class NullInput { }

现在只剩下在main()方法中连接它,如下所示:

1
2
3
4
5
6
7
8
9
    static void Main(string[] args)
    {
        var factory = new CommandFactory();
        factory.RegisterCommands(typeof(Program).Assembly);

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

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

1
2
3
4
5
6
  ------------------------
    Available commands:
  ------------------------
     add -> Add object
    list -> List objects
  ------------------------

以及"add"命令的示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
  -------------------------------------


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

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

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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();
    }
}

来源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// <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;
    }
}

PowerShell命令。

PowerShell基于在commandlets上指定的属性进行的分析,支持验证、参数集、管道、错误报告、帮助,最好是返回.NET对象以在其他commandlets中使用。

我发现一些有用的入门链接:

  • 快速入门教程
  • msdn编程指南
  • msdn上的命名空间引用

成吉思的命令行解析器可能有点过时,但它的功能非常完整,对我来说工作得很好。


请使用apache commons cli api的.net端口。这很管用。

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

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

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


一个非常简单、易于使用的特殊类,用于命令行分析,它支持默认参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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"));
    }
}


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