views:

20617

answers:

14

When building console applications that take parameters, you can use the arguments passed to Main(string[] args). In the past I've simply indexed/looped that array and done a few regular expressions to extract the values. However, when the commands get more complicated, the parsing can get pretty ugly.

More recently, I built the world's simplest Backus-Naur Form parser in C# to parse the arguments. It does the job, but it also feels like overkill.

So I'm interested in:

  • Libraries that you use
  • Patterns that you use

Assume the commands always adhere to common standards such as answered here.

+4  A: 

A previous discussion, split-string-containing-command-line-parameters-into-string-in-c#, may have some answers.

gimel
+13  A: 

Look at http://anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/Mono.Options/

abatishchev
Well, the Options class of that project is tagged with: [Obsolete ("This class is being obsoleted, we suggest that you use NDesk.Options library instead (ndesk.org/options")] - http://ndesk.org/Options
Tormod Fjeldskår
NDesk options has a very nice API
I will add another vote for NDesk it works well, is non-intrusive and well documented.
Terence
Mono.GetOptions is very old, NDesk.Options is much nicer (or Mono.Options if you prefer, its the same class, here: http://anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options/Mono.Options/Options.cs )
Matt Enright
@Matt, Yea, it is. My question is very old too. One year and one week old :)
abatishchev
link doesn't work. answer is not helpful.
Adam Oren
@Adam Oren: my answer is 1 year and 1 month old! mono trunk's structure was refactored. That code is now placed to http://anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/Mono.Options/
abatishchev
@Tormod: it's Mono.GetOptions which is obsolete, not Mono.Options. Mono.Options is still maintained.
jonp
+3  A: 

I like that one, because you can "define rules" for the arguments, needed or not,...

or if you're a Unix guy, than you might like the GNU Getopt .NET port.

Peter
+11  A: 

The WPF TestApi library comes with one of the nicest command line parsers for C# development. I highly recommend looking into it, from Ivo Manolov's blog on the 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);
sixlettervariables
+1. Command-line parsing is something really should come from the vendor (ie Microsoft) rather than via a 3rd party tool, even if the vendor's support comes in a round-about manner.
Joel Coehoorn
That said, the accepted answer (mono) is the next best thing.
Joel Coehoorn
@Joel, what part is that important that command line parsing has to be from vendor? What are your reasons?
macias
@marcias: I think he means it probably should have been Out of the Box...like a lot of things :)
sixlettervariables
+3  A: 

Hi,

There is a command line argument parser at http://www.codeplex.com/commonlibrarynet

It can parse arguments using
1. attributes
2. explicit calls
3. single line of multiple arguments OR string array

It can handle things like the following:

-config:Qa -startdate:${today} -region:'New York' Settings01

It's very easy to use.

+2  A: 

This is a handler I wrote based on the Novell Options class.

This one is aimed at console applications that execute a while (input !="exit") style loop, an interactive console such as an FTP console for example.

Example usage:

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

And the source:

/// <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;
    }
}
Chris S
+1  A: 

Genghis Command Line Parser may be a little out of date, but it is very feature complete and works pretty well for me.

devdimi
A: 

Here is another option on the Google Code Library:

Google Code

It uses reflection to populate a data class from the input command line args.

Qwerty
You do realize this one is written in Java, right?
Adam Haile
Can't you just put an N infront of it, like nHibernate, nUnit?
Dead account
+5  A: 

I wrote a C# command line argument parser a while back. Its at: http://www.codeplex.com/CommandLineArguments

PeterH
+20  A: 

I would strongly suggest using NDesk.Options (Documentation) and/or Mono.Options (same API, different namespace). An example from the documentation:

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;
}
jonp
A: 

Well, it ain't that pretty but in a pinch I've used this.

 static void Main(string[] args)
    {
        // join into one string
        string argString = string.Join(" ", args);

        // Split the string at the slash and loop through 
        foreach (string item in argString.Split('/')) {


            // pull a key=value pair split by the first white space
            int placeOfFirstSpace = item.IndexOf(' ');
            string thisKey = item.Substring(0, placeOfFirstSpace);
            string thisVal = item.Substring(placeOfFirstSpace);

            // eval the key or slash-switch option ("/key")
            switch (thisKey) {
                case "target":
                    // ... 
                    break;
                case "source":
                    //
                    break;
            }
        }



    }
Richard
+1  A: 

There are numerous solutions to this problem. For completeness and to provide the alternative if someone desires I'm adding this answer for two useful classes in my google code library.

The first is ArgumentList which is responsible only for parsing command line parameters. It collects name-value pairs defined by switches '/x:y' or '-x=y' and also collects a list of 'unnamed' entries. It's basic usage is discussed here, view the class here.

The second part of this is the CommandInterpreter which creates a fully-functional command-line application out of your .Net class. As an example:

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)
        { ... }

With the above example code you can run the following:

Program.exe DoSomething "string value" 5

It's as simple as that or as complex as you need it to be. You can review the source code or download the binary.

csharptest.net
+2  A: 

Looks like everybody has their own pet command-line parsers, figure I had better add mine as well :).

http://bizark.codeplex.com/

This library contains a command-line parser that will initialize a class with the values from the command-line. It has a ton of features (I've been building it up over many years).

From the documentation...

Command-line parsing in the BizArk framework has these key features:

  • Automatic initialization: Class properties are automatically set based on the command-line arguments.
  • Default properties: Send in a value without specifying the property name.
  • Value conversion: Uses the powerful ConvertEx class also included in BizArk to convert values to the proper type.
  • Boolean flags: Flags can be specified by simply using the argument (ex, /b for true and /b- for false) or by adding the value true/false, yes/no, etc.
  • Argument arrays: Simply add multiple values after the command-line name to set a property that is defined as an array. Ex, /x 1 2 3 will populate x with the array { 1, 2, 3 } (assuming x is defined as an array of integers).
  • Command-line aliases: A property can support multiple command-line aliases for it. For example, Help uses the alias ?.
  • Partial name recognition: You don’t need to spell out the full name or alias, just spell enough for the parser to disambiguate the property/alias from the others.
  • Supports ClickOnce: Can initialize properties even when they are specified as the query string in a URL for ClickOnce deployed applications. The command-line initialization method will detect if it is running as ClickOnce or not so your code doesn’t need to change when using it.
  • Automatically creates /? help: This includes nice formatting that takes into account the width of the console.
  • Load/Save command-line arguments to a file: This is especially useful if you have multiple large, complex sets of command-line arguments that you want to run multiple times.
Brian
A: 

I would suggest the open-source library CSharpOptParse. It parses the command line and hydrates a user-defined .NET object with the command-line input. I always turn to this library when writing a C# console application.

Stuart Lange