Command Line Parsing on Windows with Mono.Options

When you have command line arguments to parse (whether on Windows or Linux), the place to look is Mono.Options. To date, I have not found anything better to get the job done. It is a single C# file if you do not want to download the entire Mono Library to use it. I was first introduced in Mono 2.2. The equivalent product is NDesk.Options by Jonathan Pryor. His single Options.cs file has been implemented as Mono.Options.

To demonstrate how easy command line parsing can be, I’m going to show you is part of the source for RoundhousE DB Migrations. RoundhousE has 2 required arguments and 22 total arguments. So I needed something that would make that super easy. And the ramp up time had to be less than 10 minutes. Mono.Options wins on both of these requirements.

Setting Your Command Line Options

OptionSet option_set = new OptionSet()
    .Add("?|help|h", "Prints out the options.", option => help = option != null)
    .Add("d=|db=|database=|databasename=",
       "REQUIRED: DatabaseName - The database you want to create/migrate.",
       option => configuration.DatabaseName = option)
    .Add("f=|files=|sqlfilesdirectory=",
       "REQUIRED: SqlFilesDirectory - The directory where your SQL scripts are.",
       option => configuration.SqlFilesDirectory = option)
    .Add("s=|server=|servername=|instance=|instancename=",
       string.Format("ServerName - The server and instance you would like to run on. (local) and (local)\\SQL2008 are both valid values. Defaults to \"{0}\".",
           ApplicationParameters.default_server_name),
       option => configuration.ServerName = option)
;

I am setting up the arguments here and what they will be assigned to. The first item in .Add is the command line argument (parameter). For more than one, you add a pipe “|”. Jonathan Pryor mentions in his “So you want to parse a command line…” post that parameters are passed following ways:

  • Parameters of the form: -flag, --flag, /flag, -flag=value, --flag=value, /flag=value, -flag:value, --flag:value, /flag:value, -flag value, --flag value, /flag value.
  • "boolean" parameters of the form: -flag, --flag, and /flag. Boolean parameters can have a `+' or `-' appended to explicitly enable or disable the flag (in the same fashion as mcs -debug+). For boolean callbacks, the provided value is non-null for enabled, and null for disabled.
  • "value" parameters with a required value (append `=' to the option name) or an optional value (append `:' to the option name). The option value can either be in the current option (--opt=value) or in the following parameter (--opt value). The actual value is provided as the parameter to the callback delegate, unless it's (1) optional and (2) missing, in which case null is passed.

The second item in .Add is description. When you want to provide information about the argument, put a nice description in here. If you take a look at my last .Add, I have set the description to “ServerName – The server and instance you would like to run on. (local) and (local)\SQL2008 are both valid values. Defaults to ‘(local)’.” It gives enough information to allow someone to understand what is going on. It also lets you know that there is a default setting.

The third item in .Add is what you want the argument set to when found. With the first .Add after help I am setting configuration.DatabaseName to the argument’s (option) value.

Parsing the Command Line

try
{
    option_set.Parse(args);
}
catch (OptionException)
{
    show_help("Error - usage is:", option_set);
}

This is how easy it is to parse the command line. Once your OptionSet is ready, all you need to do is call Parse and pass it the arguments.

Showing Help

if (help)
{
    const string usage_message = 
        "rh.exe /d[atabase] VALUE /[sql]f[ilesdirectory] VALUE " +
        "[ /s[ervername] VALUE ]";
    show_help(usage_message, option_set);
}

If the user asks for help, you set help to true. Then you set up a usage message and pass that to a function that shows both your message and the descriptions of all the arguments.

Checking For Required Items

if (configuration.DatabaseName == null)
{
    show_help("Error: You must specify Database Name (/d).", option_set);
}

When you have required items, you can check to see if the user has sent in an argument properly. Then if not, you send them to the show_help function with a message about a necessary argument not being set.

Show Help Function

public static void show_help(string message, OptionSet option_set)
{
    Console.Error.WriteLine(message);
    option_set.WriteOptionDescriptions(Console.Error);
    Environment.Exit(-1);
}

In show_help you first show the message, then you write out the options and their descriptions.

That’s all great, but what does it look actually like?  Let’s run “rh /?” and see what we get for output.

rh.exe /d[atabase] VALUE /[sql]f[ilesdirectory] VALUE [ /s[ervername] VALUE ]
  -?, --help, -h             Prints out the options.
  -d, --db, --database, --databasename=VALUE
      REQUIRED: DatabaseName - The database you want to create/migrate.
  -f, --files, --sqlfilesdirectory=VALUE
      REQUIRED: SqlFilesDirectory - The directory where your SQL scripts are.
  -s, --server, --servername, --instance, --instancename=VALUE
      ServerName - The server and instance you would like to run on. (local) and (local)\SQL2008 are both valid values. Defaults to "(local)".

Conclusion

Mono.Options definitely wins the Rock Star Tool Award*! Mono.Options makes what would be a nightmare to parse a joy and accomplished without a lot of work. If you have more than one argument to parse with your command line, I would definitely give it a look.

* The rock star tool award means a tool is very powerful but takes less than 10 minutes to learn how to use.

Note: I am always on the lookout for rock star tools that make it easier and more enjoyable to accomplish things in development that need to be done. If you have a rock star that you want me to look at, please feel free to contact me.

kick it on DotNetKicks.com Shout it

Print | posted @ Sunday, November 22, 2009 12:49 PM

Comments on this entry:

Gravatar # re: Command Line Parsing on Windows with Mono.Options
by Dima Pasko at 11/24/2009 2:04 AM

I am using NConsoler (http://nconsoler.csharpus.com/) instead
Comments have been closed on this topic.