Search code examples
c#argumentsparameter-passing.net-6.0system.commandline

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


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


Solution

  • 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").