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

C# command line parsing

, 1 Sep 2008
Rate this:
Please Sign up or sign in to vote.
A simple base class to extract command line options.

Introduction

I love console applications. They're quick to write, and effective when trying out new code or testing code snippets. They're also invaluable when writing utility functions that you may possibly want to script or run unattended. I find that I write any number of them in a day. What one does tend to discover though, is that you end up writing the same pieces of code over and over, and that code is invariably for command line parsing.

This project started as a few modest snippets of helper code, and gradually grew into the library it is today. It has become immeasurably valuable to me, and I am hoping that someone else may find a use for it too.

Why another parser?

There are many command line parsers out there, many of them on CodeProject itself. I did not intentionally set out to develop this library – it kind of ‘evolved’. Now that it has developed to a certain level of maturity, I have compared it to some of the others out there, and found mine to be simpler to use, more expressive, and far neater in source. Anyway, try it out. You may just like it!

Using the code

Once the library has been ‘linked’ into your project (more on that later), it’s simply a matter of decorating the start up class with a few attributes, and the library does the rest. I have defined two attributes, those being ApplicationAttribute and OptionAttribute.

ApplicationAttribute

The ApplicationAttribute defines how the application, in general, will react to command line options, for example:

[Application(
    HelpOptions=new string[] {"?", "help"},
    OptionPrefixes=new char[] {'/', '-'},
    ThrowParsingExceptions=false)]
public class Program : CommandLineBase
{
    static void Main(string[] args) { new Program(args); }

    private Program(string[] args) : base(args) 
    {
        if ((Options.Count == 0) &&
            !ShowingHelp)
            ShowHelp();

        if (ParseErrors)
            WriteLine("Errors occurred!");
    }
}

HelpOptions is a string array containing the list of ‘triggers’ that will invoke the help screen. OptionPrefixies are the forward slashes or dashes (or both) that are expected to precede each command line argument. Using the example above, entering a /? or -help or any other combination of the two will invoke the help screen.

ThrowParsingExceptions will determine whether or not an exception is to be thrown when a command line could not be parsed. This will vary depending on how you want to handle an invalid command line. If you choose to switch the throwing of exceptions off, you can test whether errors occurred by looking at the ParseErrors property.

The example also shows the use of the ShowingHelp property (determine whether the help screen was invoked), the ShowHelp method (force the help screen to display), and the WriteLine method, which will emit the specified text. It is possible to redirect output from the standard console to another source. This is done by providing a TextWriter instance to the base constructor, so it is always advisable to use the base class’s WriteLine rather than a Console.WriteLine.

OptionAttribute

The snippet above is pretty useless, as no command line options are recognised. We use the OptionAttribute for that. Have a look at the next example.

[Application(
    HelpOptions=new string[] {"?", "help"},
    OptionPrefixes=new char[] {'/', '-'},
    ThrowParsingExceptions=false)]
[Option("Confirm",
    FormatPattern="y|n",
    FormatDisplay="y/n",
    ShortDescription="Confirmation",
    LongDescription="Choose either 'y' or 'n' to confirm or deny.",
    ValuePresence=OptionAttributeValuePresence.MustHaveValue,
    IsOptional=true,
    Value="y")]
[Option("TheFile",
    IsAnonymous=true,
    ShortDescription="FileName",
    LongDescription="The file to process",
    ValuePresence = OptionAttributeValuePresence.MustHaveValue,
    IsOptional = false)]
public class Program : CommandLineBase
{
    static void Main(string[] args) { new Program(args); }

    private Program(string[] args) : base(args) 
    {
        if (ParseErrors)
        {
            base.WriteLine("Errors occurred!");
            return;
        }

        if ((Options.Count == 0) &&
            !base.ShowingHelp)
            ShowHelp();

        if (!ShowingHelp)
            WriteLine(
                "Must we do it to file name '{0}'? {1}!",
                Options["TheFile"].Value,
                Options["Confirm"].Value);
    }

Now, it looks a little more interesting! We accept two command line arguments. One of them is a file name, the other some kind of confirmation switch. Each option is identified by a name ("Confirm" and "TheFile", in this example). If you have a look at the configuration, the file has IsAnonymous set to true, meaning it doesn't need any prefix or switch before it. You just enter it as-is on the command line. The "Confirm" option does require its switch. It also uses a Regular Expression to determine what values are permissible. An appropriate command line for this configuration would be:

Command line result of TestCmdApp.exe c:\WINDOWS\Santa Fe Stucco.bmp /confirm:n

If we invoke the Help screen now, we now see that the library has pulled all the relevant information together and displays it to us.

Command line result of TestCmdApp.exe /?

The named parameters on the OptionAttribute attribute are:

  • Name
  • The key by which the option is indentified, both in code and on the command line.

  • Value
  • Used both to read the value that was entered on the command line, and to set the default value that should be assigned, should this option not appear on the command line.

  • Values
  • If the same option appears multiple times, an array of values is built up. The Value property always returns the first value found.

  • ShortDescription and LongDescription
  • Friendly descriptions of what the switch does, to be used on the help screen.

  • FormatPattern
  • A Regular Expression pattern to validate the value. If the validation fails, an error occurs (which will result in a CommandLineParsingException being thrown if the ThrowParsingExceptions parameter is set to true on the ApplicationAttribute).

  • FormatDisplay
  • The text to display on the help screen, as Regular Expressions are generally not very meaningful to the average user. This will likely be some form of 'shorthand' to carry across the general intent of the pattern specified.

  • IsOptional
  • Whether or not the option should appear on the command. If the option is not optional, an error occurs if it is missing.

  • IsAnonymous
  • If an option is anonymous, it is expected that the option name will not precede it. This is generally used when specifying file names or paths on the command line.

  • ValuePresence
  • This can be one of three values. MustNotHaveValue means that the option name must appear on its own, without a value. MayHaveValue is used when a value may or may not accompany the option name, and MustHaveValue when you always want a value to be provided.

  • HasValue
  • This is used in code to detect whether a value was given, generally when ValuePresence is set to MayHaveValue.

  • Order
  • The numeric order in which the options will appear on the help screen.

  • IsPresent
  • Test whether an option was entered on the command line.

  • AllowMultiple
  • Specify whether multiple copies of the option can be entered on the same command line.

Different exceptions

Take note! The library can throw two different types of exceptions. The first, CommandLineParsingException, is thrown when the user enters an 'invalid' sequence of command line options. A command line is invalid if a mandatory option was not given, a format pattern validation failed, an option that was not supposed to have a value did have one,... you get the idea. You can override the throwing of this exception with the ThrowParsingExceptions parameter on the ApplicationAttribute.

The other exception, a CommandLineParserBaseException, is thrown when you, the developer, specify a conflicting or incorrect combination of OptionAttribute parameters. This one will always be thrown, regardless of the state of ThrowParsingExceptions.

In a sense (using a lot of literary license here), view the CommandLineParsingException as a runtime exception and CommandLineParserBaseException as a compiler exception.

Suggestions

I have found it quite helpful to create partial classes when writing command line apps. One class will contain the long list of attributes defining all the options, and the other class will contain the actual application code. This way, I don't clutter up my code.

Using the library

Right! Now you're ready to use the library. If you reference the CommandLineBase DLL from your application, you will need to distribute both a DLL and your exe, which may not always be desirable. There are two ways to get around this.

The first is to use the fabulous IL Merge tool. This combines both the exe and the DLL (and any other referenced libraries for that matter) into a single assembly. It does take some fiddling around with to get it working, but it does work well.

The second option is the 'cut-and-paste' option. Basically, just include all the source files right into your application project.

Both options have pro's and con's, and each are suited to different environments. I have used both approaches in various instances, and both work well.

There you have it! The best way to get to know the library is to try it out for yourself. Download it and have fun! I have included both a VS 2008 project file, and a Build.cmd file, in case you don't have Visual Studio.

I'll also keep updates at my site.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License

About the Author

Keith Fletcher
Software Developer (Senior) Educos Vision Services
Australia Australia
Keith Fletcher is an avid C# developer, who has been developing software professionally for roughly the last ten years. He started off way back with a Texas Instruments TI-89A, progressed to IBM compatibles, took a brief detour into Electrical Engineering, and finally came back home to computer software development.

Comments and Discussions

 
GeneralMy vote of 5 Pinmembersych grigoriy5-Jul-11 18:54 
QuestionBug or Feature? Pinmemberjfos7-Nov-09 4:29 
QuestionLongDescription not used? Pinmemberalasdaircs9-Mar-09 1:27 
GeneralThank you so much PinmemberMember 213228917-Feb-09 0:51 
GeneralAnother Thank You Pinmemberamgadhs11-Dec-08 23:15 
GeneralUsing DLL in VB .Net application Pinmemberludwigs3rd19-Nov-08 11:57 
QuestionBug? Pinmemberandreas_from_hamburg1-Oct-08 1:10 
AnswerRe: Bug? PinmemberKeith Fletcher28-Oct-08 10:38 
GeneralThanks PinmemberRoger Zhao9-Sep-08 22:10 
GeneralNConsoler - another command line parsing library Pinmembertihobrazov8-Sep-08 7:35 
QuestionThanks for the article, but why not use one of the goodol' GNU getopt-style implementations? [modified] Pinmemberfiend1-Sep-08 9:34 
AnswerRe: Thanks for the article, but why not use one of the goodol' GNU getopt-style implementations? PinmemberKeith Fletcher1-Sep-08 10:26 
QuestionGood Article but? Pinmemberfinal_zero1-Sep-08 8:52 
AnswerRe: Good Article but? PinmemberKeith Fletcher1-Sep-08 10:23 
GeneralRe: Good Article but? Pinmemberfinal_zero4-Sep-08 7:39 

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
Web04 | 2.8.140721.1 | Last Updated 1 Sep 2008
Article Copyright 2008 by Keith Fletcher
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid