Using the CommandLineParser NuGet package to handle your command line

A library that provides both parsing and an automatically generated help page makes dealing with the command line a lot easier

From time to time I've needed to write small console apps that accept parameters via the command line. Luckily, parsing the command line is a solved problem and there are multiple libraries out there that'll do the job for you. The one I've tended to keep coming back to is CommandLineParser. Adding this library to your console application is as simple as:

PM> Install-Package CommandLineParser

Once it's installed there's two things to do, define your command line parameters and then handle them. The first part is done by defining a class that describes the parameters you want to allow users of your console app to specify. A very simple example of this would be something like this:

using CommandLine;

namespace CommandLineParsingTest
{
    public sealed class CommandLineOptions
    {
        [Option('u', "username", Required = true, HelpText = "Your username")]
        public string Username { get; set; }
        [Option('p', "password", Required = true, HelpText = "Your password")]
        public string Password { get; set; }
        [Option('c', "concurrent", Required = false, HelpText = "Number of concurrent requests", Default = 1)]
        public int ConcurrentCount { get; set; }
    }
}

There's not exactly a lot to explain in this snippet, everything is driven by the OptionAttribute that is applied to each of the properties in the class. The first two strings are for short and long versions of the name of the command line parameter (either of which can be used when calling your consolle app) and it's probably not difficult to guess what Required, HelpText and Default do... For the sake of completness, Required indicates whether the command line parser will reject any request where the parameter isn't specified, HelpText allows you to specify text that describes what the parameter does and Default provides a, well, default value for your code to use in the event that the parameter isn't specified when your console app's run.

Now that the parameters that the console app can take have been described, we need to use CommandLineParser to parse the command line and provide the values to the application. A simple example of this is:

using CommandLine;
using System;
using System.Collections.Generic;

namespace CommandLineParsingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Parser.Default.ParseArguments(args)
                .WithParsed(opts => DoSomeWork(opts))
                .WithNotParsed((errs) => HandleParseError(errs));
        }

        private static void DoSomeWork(CommandLineOptions opts)
        {
            Console.WriteLine("Command Line parameters provided were valid :)");
        }

        private static void HandleParseError(IEnumerable errs)
        {
            Console.WriteLine("Command Line parameters provided were not valid!");
        }
    }
}

There are two things going on here, the first is the DoSomeWork method which is called if the command line is successfully parsed and the second is HandleParseError if it isn't. You can put whatever you want in HandleParseError, maybe some logging, but I'm not going to go into more detail other than to show what happens if one, or more, of the command line arguments are incorrect. If you run this app without specifying any of the parameters you'll see something like this:

CommandLineParsingTest 1.2.3.4
Copyright © My Copyright notice from AssemblyInfo.cs 2018
ERROR(S):
Required option 'u, username' is missing.
Required option 'p, password' is missing.

  -u, --username      Required. Your username

  -p, --password      Required. Your password

  -c, --concurrent    (Default: 1) Number of concurrent requests

  --help              Display this help screen.

  --version           Display version information.

Command Line parameters provided were not valid!

This is the built in output from CommandLineParser which is shown if the user gets any parameters wrong, along with the extra message that we dropped in through the call to Console.WriteLine in HandleParseError. There are two additional parameters shown in the output, --help and --version. These are built-in to the library and save you from having to do any work to implement a help page to describe your console app's parameters and version string output. One minor annoyance is that both will end up calling the WithNotParsed method in the event that no parameters are specified, at least as of version 2.3.0 of the NuGet package.

The second, and arguably more interesting method, is the DoSomeWork method which gets passed an instance of our CommandLineOptions class. If we replace the body of the method with something a bit more useful:

private static void DoSomeWork(CommandLineOptions opts)
{
    Console.WriteLine($"ConcurrentCount: {opts.ConcurrentCount}");
    Console.WriteLine($"Username: {opts.Username}");
    Console.WriteLine($"Password: {opts.Password}");
}

The output from calling the console app with this:

K:\CommandLineParsingTest\bin\Debug>CommandLineParsingTest.exe -u MyUsername -p MyPassword

Becomes:

ConcurrentCount: 1
Username: MyUsername
Password: MyPassword

Note that the parameter -c/-concurrent isn't specified on the command line but is still populated with the default value from the attribute on the CommandLineOptions class's ConcurrentCount property.

That's pretty much all there is to using this library, but for something that is a lot of work to get right it takes a lot of the work (and pain!) out of dealing with command line arguments. Are there any other libraries that are better?

About Rob

I've been interested in computing since the day my Dad purchased his first business PC (an Amstrad PC 1640 for anyone interested) which introduced me to MS-DOS batch programming and BASIC.

My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.

No Comments

Add a Comment