1

How can I get NDesk to parse multi arg lists at the command line (C#)?

I have two flags that both take multiple parameters (a list of files). So one is -j and the other is -c. But I think NDesk requires each of these parameters to be preceded with the -j/-c)

For instance I want:

%> main -j file1.j file2.j file3.j -c file4.c file5.c file6.c file7.c

And have it produce the 2 lists, one with the .j files and the other with the .c files. But, it expects every file listed this way to be preceded by the flag.

So this will work:

%> main -j file1.j -j file2.j -j file3.j -c file4.c -c file5.c -c file6.c -c file7.c

Though I'd rather have the first version.

Is there a way to do this with the NDesk.Options lib? I've read most of the documentation and I don't think that it is.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
lucidquiet
  • 6,124
  • 7
  • 51
  • 88
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Feb 07 '13 at 00:57

2 Answers2

2

There is a way to do this, but it requires little hack. NDesk uses "<>" as a special default handler. What you do is keep track of the "current" parameter and then have the default handler decide what to do with the values based on the current parameter. Here is a sample where I put the values into a dictionary.

static void Main(string[] args)
{
    string currentParameter = "";
    Dictionary<string, List<string>> parameters = new Dictionary<string, List<string>>();
    OptionSet set = new OptionSet() {
            { "c", ".c files", v => currentParameter = "c" },
            { "j", ".j files", v => currentParameter = "j" },
            { "<>", v => {

                    List<string> values;
                    if (parameters.TryGetValue(currentParameter, out values))
                    {
                        values.Add(v);
                    }
                    else
                    {
                        values = new List<string> { v };
                        parameters.Add(currentParameter, values);
                    }

                }
            }
        };

    set.Parse(args);
    foreach (var parameter in parameters)
    {
        Console.WriteLine("Parameter: {0}", parameter.Key);
        foreach (var value in parameter.Value)
        {
            Console.WriteLine("\t{0}", value);
        }
    }
}

The output is:

Parameter: j
file1.j
file2.j
file3.j
Parameter: c
file4.c
file5.c
file6.c
file7.c

Marc
  • 3,905
  • 4
  • 21
  • 37
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • Technically [the spec](http://www.ndesk.org/doc/ndesk-options/NDesk.Options/OptionSet.html) supports 'greedy' options, but it looks like you have to override `OptionSet` and write it yourself to support this feature; otherwise use this solution. – drzaus Jan 20 '14 at 22:15
1

Another way to do this is to override OptionSet.Parse as outlined here

That example describes a more thorough "concatenation/append" approach that will parse all formats, but if you are just interested in handling -flag value1 value2 value3 ... valueN then you can use:

public class MultiOptionSet : OptionSet
{
    private string lastArg;
    private Option lastOption;

    protected override bool Parse(string argument, OptionContext c)
    {
        // based on example in http://www.ndesk.org/doc/ndesk-options/NDesk.Options/Option.html#M:NDesk.Options.Option.OnParseComplete(NDesk.Options.OptionContext)

        string f, n, s, v;
        bool haveParts = GetOptionParts(argument, out f, out n, out s, out v);

        // reset placeholder for next multiple if we are looking at a flagged argument name
        if( haveParts )
        {
            lastArg = f + n;
        }
        // are we after a flagged argument name, without parts (meaning a value)
        else
        {
            // remember for next time, in case it's another value
            if( null != c.Option ) lastOption = c.Option;
            // but if the 'last arg' wasn't already provided, we reuse the one we set last time
            else
            {
                c.Option = lastOption;
                c.OptionName = lastArg;
            }

            c.OptionValues.Add(argument); // add the value to be invoked
            c.Option.Invoke(c); // perform the 'setter'
            return true;
        }

        return base.Parse(argument, c);
    }
}
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • hm...seems to handle `/flag:value1 value2 value3` and `-flagvalue1 value2 value3` just fine... – drzaus Jan 21 '14 at 16:15