Skip to content
Giacomo Stelluti Scala edited this page Jul 7, 2015 · 86 revisions

As you can see, Version 2.0 is publicly available as an alpha. This is not only a matter of stability, but it means that the API is subject to change until it is promoted beyond pre-release status.

From a git repository perspective, 2.0 alpha lives in the master branch, while the latest stable branch can be found in stable-1.9.71.2; previous versions are tagged.

Public branches may exist as part of development, but are normally of collaborator's interest only.

I recommend you refer to unit tests of the Parser class to get started.

Get it

I recommend cloning the repository since I will not push the alpha branch to NuGet very often. Each time I publish the alpha, I'll warn Twitter followers.

PM> Install-Package CommandLineParser -Pre

I will sometimes hold off on listing the latest alpha versions on NuGet that I mention in tweets, so take note of the version and install it via the Package Manager Console. For example, to install 2.0.9-alpha:

PM> Install-Package CommandLineParser -Version 2.0.9-alpha

Overview

The package doesn't have any dependencies. The reference to FSharp.Core.dll is needed only at compile time or at runtime for F# projects (which contain this reference by default).

The Parser is activated from the Parser class, defined in the CommandLine namespace. I suggest that you use the pre-configured Default singleton, constructing an your own instance only when really required.

using CommandLine;

// (1) default singleton
var result = Parser.Default.ParseArguments<Options>(args);

// (2) build and configure instance
var parser = new Parser(with => with.EnableDashDash = true);
var result = parser.ParseArguments<Options>(args)

In the latter case the Parser(Action<ParserSettings>) constructor was called to configure the parser via the ParserSettings instance.

The CommandLine.Text namespace contains everything you need to create a user friendly help screen. This is done in a completely automated manner if ParserSettings.HelpWriter is set.

The default instance Parser.Default initializes with ParserSettings.HelpWriter set to Console.Error.

These features are completely optional, so you can take advantage of and customize features to any degree you like.

Options

Version 2.0 uses only two attributes to describe option syntax: Option and Value·

Option works much like in previous versions, but it can be applied to scalar or sequence values (IEnumerable<T>).

When applied to sequences you can also define Min and Max properties to specify a range of values.

Value resembles ValueOption and ValueList from previous versions. Akin the new Option attribute, they can be applied to sequences, and now support the Required property too.

[Value] Attribute

Values are partitioned by index. For example:

class Options {
  [Value(0)]
  public int IntValue { get; set; }

  [Value(1, Min=1, Max=3)]
  public IEnumerable<string> StringSeq { get; set; }

  [Value(2)]
  public DoubleValue { get; set; }
}

So long as you supply values, they will be set to corresponding properties:

$ app 10 str1 str2 str3 1.1

If you omit Min and Max constraints, all available values will be captured by the sequence. There's no point defining a Value attribute with a higher index than that of a sequence Value which lacks a Max range constraint:

class Options {
  [Value(0)]
  public int IntValue { get; set; }

  [Value(1)]
  public IEnumerable<string> StringSeq { get; set; }

  // all values captured by previous specifications,
  // this property will never receive a value
  [Value(2)]
  public DoubleValue { get; set; }
}

[Option] Attribute

Omitting an option name is perfectly acceptable. In such a case, the long name will be inferred from the member's name.

class Options {
  [Option]
  public bool Version { get; set; }
}

This allows:

$ app --version

Option attribute also supports a Separator property to mimic the deprecated OptionList behavior when applied to sequences.

class Options {
  [Option('t', Separator=':')]
  public IEnumerable<string> Types { get; set; }
}

This allows:

$ app -t int:long:string

Capturing Sequences

As mentioned above, you can apply both new attributes to IEnumerable<T> (where T is a CLR built-in data type such as string, int, etc).

You can also specify a Min constraint or a Max constraint alone: this means you only want check for minimum or maximum number of elements. Breaking a constraint will cause parsing to fail.

You could overlay Min=1 with Required=true but there's no point in doing so because the parser will check Required before the Min constraint. In this case you should favor Min=1 whilst Min=1, Max=1 should be avoided (even though they will behave as expected) in favor of Required=true.

Parsing

Parsing is as easy as writing one line of code:

var result = Parser.Default.ParseArguments<Options>(args);

The above is a basic usage scenario (without verbs) utilizing the default pre-built instance to parse input arguments and using the rules specified in Options (as described above).

The result variable (defined here using implicit typing) is of type ParserResult<T>, or more specifically ParserResult<Options>.

If parsing succeeds, you'll get a the derived Parsed<T> type that exposes an instance of T through its Value property.

If parsing fails, you'll get a derived NotParsed<T> type with errors present in Errors sequence.

Even though it's possible to examine the Errors sequence, it's recommended to quit with an appropriate error code. The library help subsystem will print out usage and error descriptions automatically. The parser default instance is configured to output this text to Console.Error.

This small hierarchy is modeled like an F# discriminated union. You don't need a type cast to discover which derived type you received, you can simply examine the Tag property.

Two convenient extension methods are provided to help you access values:

var result = Parser.Default.ParseArguments<Options>(args)
  .WithParsed(options => ...) // options is an instance of Options type
  .WithNotParsed(errors => ...) // errors is an sequence of type IEnumerable<Error>

These methods accept a System.Action lambda, but if you prefer another approach, you can transform the parser result into any other value using Return(...):

// you can directly turn the result into an exit code for example
int Main(string[] args) {
  return Parser.Default.ParseArguments<Options>(args)
    .Return(
      options => RunAndReturnExitCode(options),
      _ => 1);
}

Verbs

To take advantage of verb commands simply an option class for each verb decorated with [Verb] attribute:

[Verb("add", HelpText = "Add file contents to the index.")]
class AddOptions { //normal options here
}
[Verb("commit", HelpText = "Record changes to the repository.")]
class CommitOptions { //normal options here
}
[Verb("clone", HelpText = "Clone a repository into a new directory.")]
class CloneOptions { //normal options here
}

A this point you have to use a proper ParserArguments<T1, T2...> overload that accepts more than one type (without using the overload with variadic arguments, the library define versions with up to 16 type parameters):

var result = Parser.Default.ParseArguments<AddOptions, CommitOptions, CloneOptions>(args);

In this case the T Value property of ParserResult<T> will be object but will contain the proper instance if parsing succeeds or NullInstance if fails.

The only change with normal parsing is the requirement to query the Parsed<object>.Value property and invoke the application logic written to handle a specific verb.

An helper extension method is provided to simplify this task:

Parser.Default.ParseArguments<AddOptions, CommitOptions, CloneOptions>(args)
  .WithParsed<AddOptions>(opts => ...)
  .WithParsed<CommitOptions>(opts => ...)
  .WithParsed<CloneOptions>(opts => ...)
  .WithNotParsed(errs => ...)

Coherently with ParseArguments<T1, T2, ...>() overloads used for verbs, you can take advantage also of Return<T1, T2, ...>(). Like in the sample with a single target instance, here we turn the parsed verb into an exit code:

int Main(string[] args) {
  return Parser.Default.ParseArguments<AddOptions, CommitOptions, CloneOptions>(args)
    .Return(
      (AddOptions opts) => RunAddAndReturnExitCode(opts),
      (CommitOptions opts) => RunCommitAndReturnExitCode(opts),
      (CloneOptions opts) => RunCloneAndReturnExitCode(opts),
      errs => 1);
}

In this scenario the parser supplies you an additional help verb that allows:

$ app help clone

In this way application users can display specific verb command help screen.

$ app help

Or will display an help screen for available verbs.

Immutable Target

If you develop according to functional programming, you probably won't define a mutable type like the ones presented in samples.

You're free to define an immutable type:

class Options {
  private readonly IEnumerable<string> files;
  private readonly bool verbose;
  private readonly long offset;

  public Options(IEnumerable<string> files, bool verbose, long offset) {
    this.files = files;
    this.verbose = verbose;
    this.offset = offset;
  }

  [Option]
  public IEnumerable<string> Files { get { return files; } }

  [Option]
  public bool Verbose { get { return verbose; } }

  [Option]
  public long Offset { get { return offset; } ]
}

The parser will rank this class as immutable by the absence of public property setters and fields.

This is the same feature that allow you to parse against an F# record:

type options = {
  [<Option>] files : seq<string>;
  [<Option>] verbose : bool;
  [<Option>] offset : option int64;
}

As you can see the options.offset record member was defined as option<int64> since the library has full support for option<'a> (full CLR name type name Microsoft.FSharp.Core.FSharpOption<T>).

Need help?

This section is the a reduced version of full wiki, still dedicated to latest stable. Sometimes old informations can be adapted to 2.0.x, but if not feel free to ask opening an issue.

Clone this wiki locally