Menu

The Lost art of Command Line Parameters

March 21, 2013 - .NET, C#, Programming

Most application frameworks/languages provide access to the Command Line parameters passed to your application. Generally it is passed to your application as either a string or an array of strings. What you do not get automatically is the functionality to parse out switches.

Command-line parameters used to be the only way to communicate with a program. Fundamentally, the command line was your UI into the program. Different platforms took different approaches. Unix-like systems typically take the “invasive” route; they replace wildcards and then pass the resulting command line to the application. This means that you don’t have to do any shell expansion of wildcards (as it is known) but you have to account for the fact that your command line could include a lot of files. It’s a trade-off, really. Either way, I figured for the purposes of this library, we could stick to the platform- if the program is run with a wildcard, you’ll see the wildcard on windows, but it will have been expanded if you run the same program on Linux. It might be worth adding an option to “auto-expand” wildcards- just for consistencies sake, but that seems like a post for another day.

Either way, most applications also include flags and switches. This is more a De Facto standard that has cropped up- there is no hard and fast rulebook about what flags and switches are or how you are supposed to pass arguments, which can cause no end of confusion when it comes to reading application documentation. the .NET language just gives you the string, and leaves it up to you to decide how to interpret it. Some language libraries provide functionality to parse the Command Line appropriately, such as Python. C# doesn’t come with such a class…. So let’s make one!

First we need to determine what exactly can exist in a command line. My method allows for two things: Switches, and arguments. A Switch can include an argument, separated from the switch with a colon. For example:

In this case, we have three switches- switch, sw, and doall. The first two include an argument. My “syntax” allows for quotes in the arguments of switches as well as the “loose” arguments. We will evidently need classes to represent and parse Arguments, and another one for Switches. The parsing can be done sequentially. Although it’s not a recommended best practice, I chose to use by reference parameters in the class constructors. In order to keep things generic and accessible, both Switches and Arguments will derive from a CommandLineElement abstract class, which will force each base class to implement toString(). the ArgumentItem class will be used for parsing both “loose” arguments, as well as arguments found after a switch.

Arguments

Arguments are simple- if the first letter of the position is a quote, we look for the next quote that isn’t doubled up. Otherwise, we look for either the next whitespace or the end of the string. Each argument only needs the actual argument value.

The constructor is where the important stuff happens. the by reference parameter is used to define the starting position, and we update it when the constructor returns to point at the character after the argument. The class also defines some statics for implicit conversions to and from a string.

Now that we have the Argument class, we can define the Switch class. The actual syntax of switches often depends on the application but also seems to depend on the platform. for example, Linux tools favour the hyphen for single letter flags, and double hyphens for multi-character flags. Switches are also called flags. forward slash is not generally used as a switch or flag indicator. Windows platforms prefer the forward slash but generally allow for single hyphens as well. We aim to support all three syntaxes, and make the client application not have to worry about which it is. We also add support for arguments- a switch can be specific as such:

The element after the colon will be parsed as an argument and attached to the switch itself. But enough waffling- on to the Switch:

With the basic parsing logic completed, we need to consider how we want this to be used. Best way is to think of how we would like to use them:

Some basic take-aways from this. First, the Core Parser Object needs to provide an Indexer. In the above example, we see it is accessing Switches by passing in the Switch name. Other possibilities include using direct numeric indexes to refer to any argument- much like you would access elements in the framework provided args[] String array. Another possibility is to have the Argument of a switch auto-populate, rather than be null, when accessed:

Have something to say about this post? Comment!