C# System.Commandline: How do I add an argument to a command so that I can pass a value to it?

507 views Asked by At

The following simple Program.cs expects a single argument for a defined root command:

using System.CommandLine;

var inputArgument = new Argument<string>(
  name: "--input", 
  description: "input any value and print it out");

var rootCommand = new RootCommand();
rootCommand.AddArgument(inputArgument);
rootCommand.SetHandler((inputArgumentValue) =>
{
  Console.WriteLine($"{inputArgumentValue}");
}, inputArgument);

rootCommand.Invoke(args);

I would expect calling this with the following parameter: --input "Hello World" to print out Hello World in the shell. However I get the following error:

Unrecognized command or argument 'Hello World'.

When I replace the Argument class with the Option, it works as expected:

using System.CommandLine;

var inputArgument = new Option<string>(
  name: "--input", 
  description: "input any value and print it out");

var rootCommand = new RootCommand();
rootCommand.AddOption(inputArgument);
rootCommand.SetHandler((inputArgumentValue) =>
{
  Console.WriteLine($"{inputArgumentValue}");
}, inputArgument);

rootCommand.Invoke(args);

What did I misunderstand about the Argument class? Why can I not pass an argument with a value to it?


I want to use the argument class instead of options due to its other properties. I am using .NET 6.0 and System.CommandLine version 2.0.0-beta4.22272.1

1

There are 1 answers

3
Guru Stron On BEST ANSWER

Check out the Command-line syntax overview for System.CommandLine docs.

It defines options as:

An option is a named parameter that can be passed to a command. The POSIX convention is to prefix the option name with two hyphens (--).

And arguments as:

An argument is a value passed to an option or a command.

So basically argument is a nameless positional parameter passed to command or option, i.e. for your first snippet valid call would be:

appName "Hello World"

You can of course add 2 arguments:

var inputArgument = new Argument<string>(
    name: "input", 
    description: "input any value and print it out");
var inputArgument2 = new Argument<string>(
    name: "input2", 
    description: "input any value and print it out");

var rootCommand = new RootCommand();
rootCommand.AddArgument(inputArgument);
rootCommand.AddArgument(inputArgument2);
rootCommand.SetHandler((inputArgumentValue, inputArgumentValue2) =>
{
    Console.WriteLine($"{inputArgumentValue} - {inputArgumentValue2}");
}, inputArgument, inputArgument2);

Then your appName --input "Hello World" invocation will result in handler getting 2 values --input for inputArgumentValue and "Hello World" for inputArgumentValue2.

But I would argue using Option<string> (the second snippet) should be more correct approach (also it will allow passing values delimited with =: appName --input="Hello World").