Skip to content

Parsing Command Args in .NET

All your commands are belong to us

2017-06-18

The Command Line - Learn to Love It

I like the web. I like GUIs but there is something about just 'talking to the computer' in a low level way without a lot ceremony - the command line interface. I think this is a result of my first experience with computers playing Orgeon Trail, drawing with Logo and trying 'logic' with the goto Basic in our classroom on the Apple ][. I seem to have a couple of posts relating to command lines in one form or another.

Silvrback blog image sb_float_center

There are so many tools in the software industry that are command line interface (cli) based. Despite the efforts of various tools the true power of git is best realized when on the commandline. The node.js ecosystem is steeped deeply in the cli. Ever do any build or deployment automation? Chances are you've had to write script or two to get things working to your liking. GitHub makes a nice little utility called Hub that enables quick access to some commonly used features that you would normally have to use the site for.

In .NET the console application project type is a way to build your own command line interface (CLI) and the string array called args is your little bit of magic that gets bound automatically for you to use in your application. You can a number of things by consuming this array and using those values for various arguments and switches to control the "GOTOs" in your application. This works for a short while but you find over time that you'll have a collection of helper methods to try to wrangle these strings. Then you need to build cli for another purpose, then what?

Buy Instead of Build

There are a number of command line argument parsers that I've used over the years. Honestly they all have just have different ways to setting up what you want your arguments to look like - read as: they all enable friction differently. The one I usually come back to is Mono.Options but I've also visited Fluent Command Line Parser and commandline. There is also CLAP and OptGet. Tharga Console is an interesting one in that there is an interactive mode you can 'trap' the user in your application to repeat shorter commands with specific context.

Now Shipping with .NET

If you bring in the CommandLineUtils package in to your console application project you'll have yet another way to parse those arguments for your application logic to consume.

You start off by creating a application (context) to work from

const string Help = "-? | -h | --help";
var app = new CommandLineApplication(throwOnUnexpectedArg: false) {
    Name = "motocli"
};

app.HelpOption(Help);
app.Execute(args);

This is the core of the application that wires up the help system.

»  .\motocli -h

Usage: motocli [options]

Options:
  -? | -h | --help  Show help information

When you start to add application commands

var brosweCommand = app.Command("browse", config =>
{
    config.Description = "launches the default browser to motowiliams.com";
    config.HelpOption(Help);
});

More of the help system lights up with information on how to use the utility.

»  .\motocli --help

Usage: motocli [options]

Options:
  -? | -h | --help  Show help information

You can also chain more commands together by adding another command to an existing command.

var brosweCommand = app.Command("browse", config =>
{
    config.Description = "launches the default browser to motowiliams.com";
    config.HelpOption(Help);
});

brosweCommand.Command("paypal", config =>
{
    config.Description = "launches the default browser to Eric's donation page";
    config.HelpOption(Help);
});

This will enable the execution of this block by issuing motocli browse pay-pal and it can also show its "sub-help"

»  .\motocli browse --help

Usage: motocli browse [options] [command]

Options:
  -? | -h | --help  Show help information

Commands:
  paypal  launches the default browser to Eric's donation page

Use "browse [command] --help" for more information about a command.

Speaking of executing, nothing is really happening at this point. We have an application that show cli help and nothing more. When you add a OnExecute to your configuration then you get a hook to actually execute some code.

If we update our browse command

var brosweCommand = app.Command("browse", config =>
{
    config.Description = "launches the default browser to motowiliams.com";
    config.HelpOption(Help);
    config.OnExecute(() =>
    {
        Console.WriteLine("Spinning up the Http Hyper-Drive. Destination motowilliams.com");
        return 0;
    });
});

Changes the output to

»  .\motocli browse
Spinning up the Http Hyper-Drive. Destination motowilliams.com

The Command Option class is where you enable dash dash options to your cli arguments. These could be no value such as a --debug, a single value like --connectionString database=foo;server=bar or multiple values, --tags foo --tags bar

In this case, adding the debugOption configuration options with a shortname will let use us access more things that can be set in the command line.

var brosweCommand = app.Command("browse", config =>
{
    config.Description = "launches the default browser to motowiliams.com";
    config.HelpOption(Help);
    var debugOption = config.Option("--debug", "debug flag to show more output", CommandOptionType.NoValue);
    debugOption.ShortName = "d";
    config.OnExecute(() =>
    {
        Console.WriteLine("Spinning up the Http Hyper-Drive. Destination motowilliams.com");
        if (debugOption.HasValue())
        {
            System.Console.WriteLine($" - taking off");
            System.Console.WriteLine($" - plotting course");
            System.Console.WriteLine($" - ... punch it!");
        }
        return 0;
    });
});

Again without the -d flag

»  .\motocli browse
Spinning up the Http Hyper-Drive. Destination motowilliams.com

and with the -d flag we get more detail to our travel log

»  .\motocli browse -d
Spinning up the Http Hyper-Drive. Destination motowilliams.com
 - taking off
 - plotting course
 - ... punch it!

Finding the right feel for your arguments is, probably, the most challenging part. You want to use the arguments and options in the right balance. Look at other cli tools and across platforms to see what are the common threads.

I hope you found this useful.