Click here to Skip to main content
Click here to Skip to main content

CommandParser - A getopt() Inspired Command Line Parser for C# using LINQ

, 5 Sep 2009
Rate this:
Please Sign up or sign in to vote.
Command line parsing is an irritatingly repetitive problem to solve when creating command line tools. What's worse, it has been solved many times over throughout the course of computing history. This library is an attempt to solve many of the problems common to handling command line arguments when

Introduction

CommandParser for C# is a simple object that is designed to handle the bulk of the problems any creator of a command line tool will run into while dealing with command line arguments. It was inspired by, but does not faithfully copy all aspects of, the C runtime function "getopt()".

One might classify these common problems as follows:

  1. Parsing command line arguments in a user friendly manner
  2. Allowing programmers to easily specify and handle command line arguments
  3. Generating a help screen that depends directly on the available arguments
  4. Detecting unknown or invalid commands
  5. Detecting commands that are required, but were not supplied

When I say this project was inspired by getopt(), the inspiration is mostly in terms of how the command line arguments can be supplied by the user. Everything else about this project is very much NOT like getopt(). I have instead tried to make use of the latest language features available in C# to achieve a similar effect. I have in no way tried to implement a fully POSIX compliant library, so if there are any deviances from that standard, I apologize.

Background

Anyone familiar with the getopt() method should know that it does a lot of useful things, not the least of which is handling the parsing of command line arguments in a flexible way. To illustrate this point, imagine you had a program that needed to take 3 different flags, p, q, and z, and the z flag takes an argument.

With getopt, you could specify these commands in any of the following ways, and they would all be treated the same:

-p -q -z7
-p -q -z 7
-pqz7
-p -qz7
-p -qz "7"
-p -qz"7"

You can see how flexible this is. Getopt also supports the use of a long form of an argument. Thus, you could let the "-p" option also be specified with an argument like "-populate". This can make the available commands easier to remember and understand.

This is all well and good, but there are a number of ways in which I think getopt() is not, well, all that opt. These include:

  1. A cryptic, yet powerful, format for letting you specify command arguments
  2. No easy to use mechanism for generating help screens for supported commands
  3. The mechanism for dispatching commands with getopt typically involves a cumbersome switch statement
  4. No implementations, to my knowledge, are available that solve all of these problems for the .NET programmer

It is for these reasons that I decided to build this project and share it. I hope you find it useful.

Using the Code

Using the CommandParser is fairly straightforward. You essentially need to specify the arguments the parser will handle, parse the command line, and handle the results.

Specifying Arguments

The following example shows a typical argument specification for a command that is required and takes a parameter:

var parser = new CommandParser("A Sample Test Harness for CommandParser.cs");

parser.Argument("h", "host", "Specify a host ip address", "ip_addr",
                 CommandArgumentFlags.TakesParameter | CommandArgumentFlags.Required,
                 (p, v) => {
                     host = v;
                 });

In this example, we have passed 6 arguments to the "Argument" method. The first argument is the short form for the flag, the second is the long form, the third is a description for the flag that will appear in the auto-generated help screen, and the fourth is the string to use to define the parameter that this argument takes in the help screen.

Finally, the handler argument describes what to do when the command flag is encountered. This handler is a function that takes a reference to the CommandParser doing the parsing, and a string that contains the value of the argument passed to it. In the example above, we make use of a C# lambda expression to define the handler inline. Since, in practice, many command line arguments are just flags that need to be stored on a settings object for a tool, being able to specify what to do with the flag alongside its definition is very handy.

Parsing the Command Line

Once you have specified all of the arguments you want the parser to handle, telling the parser to do its work is a simple matter:

parser.Parse();

When you call the Parse() method, the command line parser will determine which flags are present, and when it encounters them, it will call the actions provided when the arguments were defined as described above.

During the parsing, any unknown commands that were detected as well as any commands that are required that weren't provided will be stored by the parser object. This lets you take appropriate action depending on how you want to handle these issues.

Handling Post-Parsing Results

In the included test harness for this project, the following code is supplied to show one way to handle program execution after the parsing completes.

if (parser.UnknownCommands.Count > 0) {
    foreach (var unknown in parser.UnknownCommands) {
        Console.WriteLine("Invalid command: " + unknown);
    }

    Console.WriteLine(parser.GetHelp());
} else if (parser.MissingRequiredCommands.Count > 0) {
    foreach (var missing in parser.MissingRequiredCommands) {
        Console.WriteLine("ERROR: Missing argument: " + missing);
    }

    Console.WriteLine(parser.GetHelp());
} else if (!showHelp) {
    Console.WriteLine("update = " + update);
    Console.WriteLine("populate = " + populate);
    Console.WriteLine("host = " + host);
    Console.WriteLine("query = " + query);
} else {
    Console.WriteLine(parser.GetHelp());
}

In this example, if we're not showing the help screen explicitly, then we execute the primary purpose of the tool. In this case, the tool outputs the result of the parsing flags, but your tools will probably do something more useful than that.

What is more interesting is the cases where we are easily able to handle both unknown commands as well as missing required commands quickly and easily. We can even display a help screen in these cases by simply calling parser.GetHelp().

The help screen provided by the CommandParser uses the argument list you specified to automatically generate help text based on the arguments you have supplied to the parser. This means that when you update your list of supported commands, the help screen will be updated automatically.

Points of Interest

I'm sure there is much that could be done to this to make it more elegant. I am still not overly happy with how argument specification turned out. It works, but I feel that it is a bit clunky and could be better. I'd love to hear any suggestions people might have for this.

History

  • V1.0 - Initial release

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

Christopher Hahn
Chief Technology Officer Appature, Inc.
United States United States
I have been developing software professionally for over 12 years. I have been a developer at ATI Research, Microsoft, a Social Bookmarking website called Faves.com, and most recently I have started a company called Appature, Inc focusing on Enterprise Marketing Management software in the Healthcare space.
 
If you find any of my submissions here useful, or even if you don't, I'd love to hear from you!

Comments and Discussions

 
QuestionUsing in Xamarin PinmemberMember 1088447014-Jun-14 6:59 
QuestionMaking it testable PinmemberPhillip Sdao14-Apr-14 6:01 
BugDoesn't work if a dash is in an option PinmemberMember 464223131-Oct-11 8:03 
Generalconform to parsing mechanism of getopt() in C PinmemberGnought19-Jun-10 11:08 
GeneralNConsoler PinmemberDima Pasko31-Oct-09 11:56 
GeneralArguments that takes parameters PinmemberDiego Boaro22-Oct-09 11:10 
GeneralRe: Arguments that takes parameters Pinmemberqwt3-Apr-10 13:23 
GeneralRe: Arguments that takes parameters Pinmembercodeproject4sba2-Jul-12 3:46 
GeneralGreat stuff, but... PinmemberDennis L. Hughes8-Oct-09 6:37 
GeneralRe: Great stuff, but... Pinmemberjoatmon0773421-Oct-09 10:24 
GeneralVery useful tool! PinmemberDrABELL18-Sep-09 9:39 
GeneralRe: Very useful tool! PinmemberChristopher Hahn29-Sep-09 11:08 
GeneralNice PinmemberRoberto Collina5-Sep-09 23:31 
GeneralRe: Nice Pinmembermvonballmo6-Sep-09 21:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 5 Sep 2009
Article Copyright 2009 by Christopher Hahn
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid