4

I want to parse a set of command line arguments that look like:

-p[project file path] -s[name 1]=[value 1] ... -s[name n]=[value n]

Where there is exactly one project p and any number of settings s.

I have tried using NDesk.Options

var set = new OptionSet {
    { "p=", "the project file", v => { /* do stuff */ } },
    { "s=", "a setting", (m, v) =>  { /* do stuff */ } },
};

and this works well in most cases, but when value is a file path (even quoted) the \ causes the parser to drop everything to right. I've hacked round this by overriding the parse method on my own OptionSet class that I've inherited from NDesk.Options.OptionSet, but I was wondering if there are any libraries that can handle this kind of functionality out of the box?

UPDATE

Sorry it wasn't the \ I think it is the : anyway a set of failing examples is:

-sSetting=C:\Temp
-sSetting="C:\Temp"
-s"Setting=C:\Temp"

They all fail with OptionException Error: Found 3 option values when expecting 2.

4

5 回答 5

5

更新:更新以处理设置值中的冒号。

好的,所以在这里您遇到了NDesk.Options的隐式默认值之一,即在多值参数中,两者:=都被视为值分隔符,这意味着Setting=C:\Path解析为 3 个值(Setting、C、\Path)而不是您的预期二。

为了解决这个问题,您只需修改-s选项定义以仅将=其视为有效分隔符,方法是编写"s={=}"而不是"s=".

原始答案,当它是关于反斜杠时。

我使用了 NDesk.Options,没有遇到引用路径和反斜杠的任何问题。

这是我的示例程序:

public static void Main(string[] args)
{
    string parsedPath = null;
    Dictionary<string, string> parsedValues = new Dictionary<string, string>();
    var set = new OptionSet() 
    { 
        { "p=", "the project path", v => parsedPath = v }, 
        { "s=", "a setting", (m, v) => { parsedValues.Add(m, v); } },
    };
    set.Parse(args);
    Console.WriteLine(parsedPath ?? "<NULL>");
    foreach (var keyValuePair in parsedValues)
    {
        Console.WriteLine(keyValuePair.Key + "::::" + keyValuePair.Value);
    }
}

你会看到你的定义和我的不同:p=意味着选项有一个必需的值,而你的定义意味着它p是一个布尔标志值。

在 p 设置或 s 设置中,我没有遇到任何关于反斜杠的问题。您能否尝试使用 0.2.1 版本的NDesk.Options运行该程序并显示哪些值失败?

以下是我运行的一些示例,均已成功解析:

-p=..\Path
-p..\Path
-pC:\Hello
-pHello\World
-p"Hello\World"
-s"Greeting=Hello\World"
-sGreeting="Hello\World"
-sGreeting=Hello\World
-sGreeting="Hello\My World"
-s"Greeting=Hello\My World"

以下是一些确实会产生另一个值得一提的结果的解析:

-sGreeting=Hello\My World -- // This gives Greeting="Hello\My" 

注意:如果这有任何改变,我使用项目中的 Options.cs 源代码文件运行 NDesk.Options,而不是使用已编译的 DLL。

于 2013-04-19T10:25:04.547 回答
3

我成功使用了一段时间的命令行解析器库。效果很好,简单,并且允许带引号的路径

于 2013-04-19T09:23:18.893 回答
2

如果您使用以下内容,所有拆分都将为您完成:

public static void Main(string[] args)
   {
       List<string> Settings = new list
       Console.WriteLine("parameter qty = {0}", args.Length);
       for(int i = 0; i < args.Length; i++)
       {
           Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);
       }

之后,一组简单的 if 应该足以根据每个参数为您提供所需的操作。您可以根据需要使用字符串匹配或正则表达式遍历参数数组中的每个参数,以查看它们符合哪些参数。例如,您可以在 for 循环中添加一些代码:

public static void Main(string[] args)
   {
       List<string> Settings = new List<string>();
       Console.WriteLine("parameter qty = {0}", args.Length);
       for(int i = 0; i < args.Length; i++)
       {
           Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);
           if (i>0)
               {
                   // this gives you a nice iterable list of settings
                   Setting.Add(args[i]);
               }
       }
       foreach(string setting in Settings)
           {
               //do the desired action
           }
    }

附录: 但是,这将仅提供基本功能(如果您像我一样来自 C++ 并且必须自己做事,从 C# 开发人员的角度来看,我现在意识到这不是很好),您需要处理自己解析变体命令(如注释中所述/p--p or /Project= -Project=,变体都需要在您制作的代码中处理)。但是,对于开箱即用的解决方案,我建议:Ndesk.OptionsMono.Options 两者的工作方式相似,如果可移植性很重要,我会倾向于 Mono.Options。

在使用中,您可以将代码更改为

var set = new OptionSet {
    { "p=|project=", "the project file", v => { /* do stuff */ } },
    { "s=|setting=", "a setting", (m, v) =>  { /* do stuff */ } },
};

这应该有望为您提供所需的功能(来自页面末尾的 ndesk 示例,甚至还有一个编码示例使用)。

希望这比我以前对您的回答更有用。

于 2013-04-19T10:21:47.507 回答
0
class Program
    {
        static void Main(string[] args)
        {
            string cmd = "car.exe -ip 1.18.4.156 -port 123";
            string hostname = "localhost";
            string port = "5505";

            string[] array = cmd.Split(' ');

            int hostnameIndex = Array.FindIndex(array, key => key == "-ip");
            int portIndex = Array.FindLastIndex(array, key => key == "-port");

            if (hostnameIndex != -1)
            {
                hostname = array[hostnameIndex + 1];
            }

            if (portIndex != -1)
            {
                port = array[portIndex + 1];
            }

            Console.WriteLine("ip :" + hostname);
            Console.WriteLine("port :" + port);

        }
    }
于 2014-09-20T17:44:40.233 回答
0

您可以在一个字符串中加入所有参数,然后可以使用正则表达式:

((?<SkeyvaluePair>-s(?<key>[^=]+)\s*=\s*(?<value>[^-]+))|(?<PkeyvaluePair>-p(?<Pvalue>[^-]+)))*

然后将字符串分成组

var groups = new Regex(yourRegex).Match(message).Groups;

而不是提取您需要的信息

var pairs = groups["keyvaluepair"].Captures.Cast<Capture>() 
于 2013-04-19T09:39:39.503 回答