Command Line Parsing on Windows with Mono.Options

When you have command line arguments to parse (whether in 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 second .Add 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.


Posted 11-22-2009 12:41 PM by Rob Reynolds
Filed under: , ,

[Advertisement]

Comments

Ronald Widha wrote re: Command Line Parsing with Mono.Options
on 11-22-2009 11:10 PM

excellent stuff. I'll make an excuse to use it in a project

csharptest.net wrote re: Command Line Parsing with Mono.Options
on 11-23-2009 1:44 PM

Interesting stuff, I found myself recently wishing this was built-in to the .Net Library.  Finding that it wasn't and being very tired of writing and re-writing this code I finally developed a similar approach.  I'm glad to see this finally starting to eek it's way into a more core library.  If your looking for a solution similar to this on the Windows platform check out:  http://csharptest.net/?p=299

Rob Reynolds wrote re: Command Line Parsing on Windows with Mono.Options
on 11-23-2009 3:54 PM

I'm using Mono.Options on the Windows platform. You are not the first person to mention the Mono part.  The Options library can be used with either Windows or Linux. Thanks for helping me see that. I will try to be more clear.

James Gregory wrote re: Command Line Parsing on Windows with Mono.Options
on 11-26-2009 4:32 AM

This is one of those things that I hand roll on every new project, and with every one think "there should be an easier way to do this!". Seems like there has been all along. Thanks for bringing this to my attention!

Alex Ford wrote re: Command Line Parsing on Windows with Mono.Options
on 09-08-2011 1:04 AM

I too am using it on the windows platform. I simply copied the .cs file out of the mono project on github and dropped it into my own project. I decided to put it to a more complicated use with a web-based command line project at http://www.u413.com and it's working beautifully!

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)