如何解析命令行参数

本文关键字:参数 命令行 何解析 | 更新日期: 2023-09-27 18:09:55

我想解析一组命令行参数,如下所示:

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

其中只有一个项目p和任意数量的设置s

我试过使用NDesk.Options

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

,这在大多数情况下工作得很好,但是当value是文件路径(甚至引号)时,'会导致解析器将所有内容移到右边。我通过重写我从NDesk.Options.OptionSet继承的OptionSet类上的解析方法破解了这一点,但我想知道是否有任何库可以处理这种开箱即用的功能?

对不起,这不是'我认为这是:无论如何一组失败的例子是:

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

他们都失败了,OptionException Error: Found 3 option values when expecting 2.

如何解析命令行参数

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

这里你遇到了NDesk的一个隐式默认值。选项,即在多值参数中,:=都被视为值分隔符,这意味着Setting=C:'Path解析为3个值(Setting, C, 'Path),而不是您期望的两个。

为了解决这个问题,您只需修改-s选项定义,将=视为有效的分隔符,写入"s={=}"而不是"s="

原来的答案,当它是关于反斜杠。

我用过NDesk。选项,而不会遇到任何带引号的路径和反斜杠的问题。

下面是我的示例程序:
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设置中。你能试着用NDesk 0.2.1版本运行这个程序吗?选项并显示哪些值失败?

下面是我运行的一些示例,它们都解析成功:

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

注意:如果这改变了什么,我运行NDesk。

使用项目中的Options.cs源代码文件,而不是编译后的DLL。

我成功地使用了命令行解析器库一段时间。

如果您使用以下命令,则所有的分割都已为您完成:

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。选项或单声道。两者的工作原理相似,我倾向于单声道。如果移植性很重要,可以选择。

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

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

这应该有希望给你你想要的那种功能(从ndesk的例子,在页面的末尾,甚至有一个编码的范例使用)。

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

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

然后将字符串分成组

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

,然后提取你需要的信息

var pairs = groups["keyvaluepair"].Captures.Cast<Capture>() 
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);
        }
    }