Command line argument parsing in .NET Core with Microsoft.Extensions.CommandLineUtils

Published by areilly on

In a recent .NET Standard/Core based library, I found myself needing to include some command line tooling. I never found a command line argument parsing library I really liked for full .NET Framework apps, so I decided to hunt for any new possibilities for a netcoreapp. Happily I found a very nice little utility that’s used natively by the dotnet CLI – The Microsoft.Extensions.CommandLineUtils package. Unfortunately there doesn’t seem to be any official documentation or much in the way of complete examples, so I thought I’d share what I’ve set up as a template for my own projects.

To just jump right in, I’ve created a example project you can try out:
https://github.com/anthonyreilly/ConsoleArgs

If you want to take a deep dive, Microsoft’s has a sources package on GitHub:  Microsoft.Extensions.CommandLineUtils.Sources

This walkthrough is using a simplified version of the example application – see the version in Github for additional comments and and options.

static void Main(string[] args)
{
    var app = new CommandLineApplication();
    app.Name = "ConsoleArgs";
    app.Description = ".NET Core console app with argument parsing.";

    app.HelpOption("-?|-h|--help");

    var basicOption = app.Option("-o|--option<optionvalue>",
            "Some option value",
            CommandOptionType.SingleValue);

    app.OnExecute(() => {
        if (basicOption.HasValue()) {
            Console.WriteLine("Option was selected, value: {0}", basicOption.Value());
        }
        else {
            app.ShowHint();
        }

        return 0;
    });

    app.Command("simple-command", (command) => {
            command.Description = "This is the description for simple-command.";
            command.HelpOption("-?|-h|--help");

            command.OnExecute(() => {
                Console.WriteLine("simple-command has finished.");
                return 0;
            });
    });

    app.Execute(args);
}

You start off by initializing the application:

var app = new CommandLineApplication();
app.Name = "ConsoleArgs";
app.Description = ".NET Core console app with argument parsing.";
app.HelpOption("-?|-h|--help");

Name should be the name of the executable itself, Description is used in the help text, and then HelpOption enables the help option and help text output.

You can then set the available arguments and options.

var basicOption = app.Option("-o|--option<optionvalue>",
   "Some option value",
   CommandOptionType.SingleValue);

The example project has some examples of Arguments, but Options is the way to go. You pass in the template, which specifies the name of the option and it’s help text at the same time.

Breaking down the template, you start with a pipe-delimited list of option flags, here we’re using -o and —option, which is a common convention: a dash and single letter, or a double-dash and a name. You can list just one, or many flags to use as needed.

After the list is a space, and then a text description of what to pass in. Here we’re using “<optionvalue>” but this is optional. The dotnet CLI seems to skip this, and just rely on the help text in the description to explain to the user what to enter after the option.

Second is a description for the help text. Finally, you set the option’s type, as a choice of NoValue (a boolean type flag), SingleValue, or MultipleValue. There are examples of each in the project.

Options can be passed with or without an equals sign (=) or enclosing quotes, although quotes are needed when passing values with spaces or delimiters.

-o value
-o=value
-o=”value”
-o “value”
-o=”value one two three”

You then create a function and pass it into OnExecute() that will parse the options and execute the work.

app.OnExecute(() =&amp;gt; {
   if (basicOption.HasValue()) {
      Console.WriteLine("Option was selected, value: {0}", basicOption.Value());
   }
   else {
      app.ShowHint();
   }

   return 0;
});

This is basically the default “command”. For a single-task kind of application you may not need more than this, but if you’re familiar with the dotnet CLI, you can do the same command/option pattern and add additional commands that in turn can have their own options and arguments.

Here we add an additional simple command, that does not have any options:

app.Command("simple-command", (command) =&amp;gt; {
   command.Description = "This is the description for simple-command.";
   command.HelpOption("-?|-h|--help");

   command.OnExecute(() =&amp;gt; {
   Console.WriteLine("simple-command has finished.");
   return 0;
  });
});

Within this new command, you can add additional options and commands as needed.

Finally, you need to actually start the application, and pass in the raw, standard command line arguments array for the utility to parse and execute the commands:

app.Execute(args);

One of my favorite things about this utility, and a great help to UX, the help text is automatically formatted for you using the Name, Description, and ExtendedHelpText properties specified in the code. If you run the full example, you’ll get something like this:

Usage: ConsoleArgs [arguments] [options] [command]

Arguments:
  argOne  App argument one
  argTwo  App argument two

Options:
  -?|-h|--help               Show help information
  -o|--option  Some option value

Commands:
  complex-command  This is the description for complex-command.
  simple-command   This is the description for simple-command.

Use "ConsoleArgs [command] --help" for more information about a command.
This is a sample console app to demostrate the usage of Microsoft.Extensions.CommandLineUtils.
Depending on your OS, you may need to execute the application as ConsoleArgs.exe or 'dotnet ConsoleArgs.dll'

With relatively little effort, you can whip up a console application with some nice user-friendly polish.

Check out the complete example here for a more complex setup and additional explanations.

Update 2017-07-31: Looks like the ASP.NET Core team has decided against publishing this package on NuGet with 2.0.0 – but it will still be available as source for you to integrate and compile yourself (See this GitHub commit message )

Stop producing Microsoft.Extensions.CommandLineUtils
This library was only intended for usage with ASP.NET Core’s tools, and not a general purpose library. After receiving bugs and questions about this, we realized that we didn’t really intend to produce and support a command-line parsing library.
We will still leave the 1.0.0 and 1.1.0 versions of this library on NuGet.org, but the current plan is to stop producing new versions of this package.
The source code is still available for internal use via Microsoft.Extensions.CommandLineUtils.Sources, however we will not be publishing this library again as a general purpose library.

Update 2017-11-01:  Good news – this has been forked as a community project, per Ben Brandt at knightlycode.com:

Nate McMaster, a software engineer on the ASP.NET Core project, has since forked the project as a community project in his free time: “This is a fork of Microsoft.Extensions.CommandLineUtils, which is no longer under active development. This fork, on the other hand, will continue release updates and take contributions.”

https://github.com/natemcmaster/CommandLineUtils

One of the interesting new features Nate has added is the parsing of response files for the command line parameters. Things are looking up.


2 Comments

Ben Brandt · November 1, 2017 at 09:28

Nate McMaster, a software engineer on the ASP.NET Core project, has since forked the project as a community project in his free time:

“This is a fork of Microsoft.Extensions.CommandLineUtils, which is no longer under active development. This fork, on the other hand, will continue release updates and take contributions.”

https://github.com/natemcmaster/CommandLineUtils

One of the interesting new features Nate has added is the parsing of response files for the command line parameters. Things are looking up.

Jeremy A Holovacs · November 16, 2018 at 07:42

your code has a lot of partially escaped HTML entities, makes for a confusing read.

Leave a Reply

Your email address will not be published. Required fields are marked *