Using a Custom IConsole with System.CommandLine
The IConsole interface defines how output is directed. The default implementation of IConsole simply redirects the output to the standard input, output and error locations. By implementing your own IConsole you can change where the output is directed.
You can learn more about creating a custom IConsole in Using a Custom IConsole with System.CommandLine
Additional option types
Configuration of each option has several possibilities. Options can be given a variety of types to ensure that only certain values can be assigned. They can be require or optional. You can provide multiple aliases and you can provide default values.
Integers
Let's add an additional option to demonstrate some of the ways you can configure an option.
This option will be an integer that has two aliases assigned. The option will has have a default value.
When declaring the aliases for the --name
option we initialized the option and then added an alias. This time we will initialize the option with both of the alias during the declaration.
new Option<int>(new[] { "--opt-int", "-i" }, description: "An integer option");
Passing in an array of strings as the first argument will assign all of the aliases in the intial declaration of the option. We have also assigned a description to the option.
Now we can make a small change and set a default value. Default values are assigned by passing a method or Action as the getDefaultValue
parameter. This action will be called to determine the default value. A nice feature of this is that you do not have to use a constant or predefined value. You could use any valid logic to determine what the default value is. In this example we will just choose a simple pre-defined value.
new Option<int>(new[] { "--opt-int", "-i" }, description: "An integer option", getDefaultValue: () => { return 47; }));
Finally we will add the new option to the existing root command.
rootCommand.Add(new Option<int>(new[] { "--opt-int", "-i" }, description: "An integer option", getDefaultValue: () => { return 47; }));
If we execute the application we can now see that the new option is included in the usage output.
Option '--name' is required.
SimpleCmdLine
Console app to demonstrate System.CommandLine
Usage:
SimpleCmdLine [options]
Options:
-n, --name <name> (REQUIRED) Name of person to greet
-i, --opt-int <opt-int> An integer option [default: 47]
--version Show version information
-?, -h, --help Show help and usage information
When executing the application with just the required --name
option will result in the default value being assigned to the --opt-int
option. If the option flag is included but no value provided then it will indicate that the value must be provided. And as expected if the option is provided and a value then the value will be assigned.
# Default value based upon *
$ dotnet run -- --name name
ParseResult: [ SimpleCmdLine [ --name <name> ] *[ --opt-int <47> ] ]
Hello name!
# Option provided but required argument not.
$ dotnet run -- --name name --opt-int
Required argument missing for option: --opt-int
SimpleCmdLine
Console app to demonstrate System.CommandLine
Usage:
SimpleCmdLine [options]
Options:
-n, --name <name> (REQUIRED) Name of person to greet
-i, --opt-int <opt-int> An integer option [default: 47]
--version Show version information
-?, -h, --help Show help and usage information
# argument provided to option
$ dotnet run -- --name name --opt-int 42
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-int <42> ] ]
Hello name!
What happens if the argument to --opt-int
is too large for an int? This is handled as well. This also happens if a string is passed in or if the argument is a floating point number.
$ dotnet run -- --name name --opt-int 5000000000000
Cannot parse argument '5000000000000' for option '--opt-int' as expected type System.Int32.
$ dotnet run -- --name name --opt-int astring
Cannot parse argument 'astring' for option '--opt-int' as expected type System.Int32.
$ dotnet run -- --name name --opt-int 3.14159
Cannot parse argument '3.14159' for option '--opt-int' as expected type System.Int32.
Other options
Adding a floating point option or any other type is similar to adding --opt-int
. We will add several types of options and see how they work.
rootCommand.Add(new Option<decimal>(new[] { "--opt-decimal" }, description: "A decimal option"));
rootCommand.Add(new Option<bool>(new[] { "--opt-bool" }, description: "A boolean option"));
rootCommand.Add(new Option<string>("--opt-string", description: "A string option", arity: ArgumentArity.OneOrMore));
None of these additional options are declared as required. Therefore they are all optional.
The --opt-decimal
behaves similar to how the --opt-int
does. The difference being the type of information that can be passed in. This option will accept floating point numbers. This could have been declared as float
and it would operate similarly.
$ dotnet run -- --name name --opt-decimal 3.14159
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-decimal <3.14159> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=
In reviewing the parse result for the command line you can see that the --opt-decimal
option was assigned the value we provided. The --opt-int
was also assigned a default value. What you do not see in the parse results is the optional parameter --opt-bool
. Despite that option being optional and not provided on the command line it appears to have a value of False
.
This demonstrates the default behavior of a bool
type argument. If the argument is not provided then it will use the default value which is false
. If we do include the --opt-bool
option then the behavior changes slightly.
$ dotnet run -- --name name --opt-decimal 3.14159
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-decimal <3.14159> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=
When the --opt-bool
option is provided but not given a value then we can see that the value default to True
. This is because we provided the option on the command line. Boolean types act like a flag. If the option is provided on the command line then it is considered to be "on" and if it is not provided it is considered to be "off".
We can also provide an explicit value to a boolean flag to ensure it has the value we want.
$ dotnet run -- --name name --opt-bool false
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-bool <False> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=
$ dotnet run -- --name name --opt-bool true
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-bool <True> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=True
--opt-string=
Arity
The last option is the --opt-string
which includes an additional parameter called arity
. Arity is the number of arguments or operands that a function or method can receive. System.CommandLine provides several options for Arity. The available options can be found in System.CommandLine.ArgumentArity
.
Some valid arity values include:
- Zero
- ZeroOrOne
- ExactlyOne
- ZeroOrMore
- OneOrMore
In addition to the predefined arity values you can also specify a minimum and maximum value by creating an instance of the ArgumentArity
class. To require a minimum of 1 argument but no more than 2 you can declare the class as follows:
new ArgumentArity(1, 2);
Passing the arguments to the option on the command line can take a few formats. Depending on how they are provided will change how the command line is interpreted.
We can try several different patterns and see what happens.
$ dotnet run -- --name name --opt-string a
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-string <a> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=a
$ dotnet run -- --name name --opt-string
Required argument missing for option: --opt-string
$ dotnet run -- --name name --opt-string a b
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-string <a> <b> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=a, b
$ dotnet run -- --name name --opt-string a --opt-string b
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-string <a> <b> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=a, b
$ dotnet run -- --name name --opt-string a --opt-string
ParseResult: [ SimpleCmdLine [ --name <name> ] [ --opt-string <a> ] *[ --opt-int <47> ] ]
Hello name!
--opt-bool=False
--opt-string=a
$ dotnet run -- --name name --opt-string a b c
Unrecognized command or argument 'c'
'c' was not matched. Did you mean '-n', or '-i', or '-h'?
$ dotnet run -- --name name --opt-string a b --opt-string c
Option '--opt-string' expects a single argument but 3 were provided.
Option '--opt-string' expects a single argument but 3 were provided.
You must be logged in to see the comments. Log in now!