How to define commands, options, and arguments in System.CommandLine
Important
System.CommandLine
is currently in PREVIEW, and this documentation is for version 2.0 beta 4.
Some information relates to prerelease product that may be substantially modified before it's released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
This article explains how to define commands, options, and arguments in command-line apps that are built with the System.CommandLine
library. To build a complete application that illustrates these techniques, see the tutorial Get started with System.CommandLine.
For guidance on how to design a command-line app's commands, options, and arguments, see Design guidance.
Define a root command
Every command-line app has a root command, which refers to the executable file itself. The simplest case for invoking your code, if you have an app with no subcommands, options, or arguments, would look like this:
using System.CommandLine;
class Program
{
static async Task Main(string[] args)
{
var rootCommand = new RootCommand("Sample command-line app");
rootCommand.SetHandler(() =>
{
Console.WriteLine("Hello world!");
});
await rootCommand.InvokeAsync(args);
}
}
Define subcommands
Commands can have child commands, known as subcommands or verbs, and they can nest as many levels as you need. You can add subcommands as shown in the following example:
var rootCommand = new RootCommand();
var sub1Command = new Command("sub1", "First-level subcommand");
rootCommand.Add(sub1Command);
var sub1aCommand = new Command("sub1a", "Second level subcommand");
sub1Command.Add(sub1aCommand);
The innermost subcommand in this example can be invoked like this:
myapp sub1 sub1a
Define options
A command handler method typically has parameters, and the values can come from command-line options. The following example creates two options and adds them to the root command. The option names include double-hyphen prefixes, which is typical for POSIX CLIs. The command handler code displays the values of those options:
var delayOption = new Option<int>
(name: "--delay",
description: "An option whose argument is parsed as an int.",
getDefaultValue: () => 42);
var messageOption = new Option<string>
("--message", "An option whose argument is parsed as a string.");
var rootCommand = new RootCommand();
rootCommand.Add(delayOption);
rootCommand.Add(messageOption);
rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>
{
Console.WriteLine($"--delay = {delayOptionValue}");
Console.WriteLine($"--message = {messageOptionValue}");
},
delayOption, messageOption);
Here's an example of command-line input and the resulting output for the preceding example code:
myapp --delay 21 --message "Hello world!"
--delay = 21
--message = Hello world!
Global options
To add an option to one command at a time, use the Add
or AddOption
method as shown in the preceding example. To add an option to a command and recursively to all of its subcommands, use the AddGlobalOption
method, as shown in the following example:
var delayOption = new Option<int>
("--delay", "An option whose argument is parsed as an int.");
var messageOption = new Option<string>
("--message", "An option whose argument is parsed as a string.");
var rootCommand = new RootCommand();
rootCommand.AddGlobalOption(delayOption);
rootCommand.Add(messageOption);
var subCommand1 = new Command("sub1", "First level subcommand");
rootCommand.Add(subCommand1);
var subCommand1a = new Command("sub1a", "Second level subcommand");
subCommand1.Add(subCommand1a);
subCommand1a.SetHandler((delayOptionValue) =>
{
Console.WriteLine($"--delay = {delayOptionValue}");
},
delayOption);
await rootCommand.InvokeAsync(args);
The preceding code adds --delay
as a global option to the root command, and it's available in the handler for subCommand1a
.
Define arguments
Arguments are defined and added to commands like options. The following example is like the options example, but it defines arguments instead of options:
var delayArgument = new Argument<int>
(name: "delay",
description: "An argument that is parsed as an int.",
getDefaultValue: () => 42);
var messageArgument = new Argument<string>
("message", "An argument that is parsed as a string.");
var rootCommand = new RootCommand();
rootCommand.Add(delayArgument);
rootCommand.Add(messageArgument);
rootCommand.SetHandler((delayArgumentValue, messageArgumentValue) =>
{
Console.WriteLine($"<delay> argument = {delayArgumentValue}");
Console.WriteLine($"<message> argument = {messageArgumentValue}");
},
delayArgument, messageArgument);
await rootCommand.InvokeAsync(args);
Here's an example of command-line input and the resulting output for the preceding example code:
myapp 42 "Hello world!"
<delay> argument = 42
<message> argument = Hello world!
An argument that is defined without a default value, such as messageArgument
in the preceding example, is treated as a required argument. An error message is displayed, and the command handler isn't called, if a required argument isn't provided.
Define aliases
Both commands and options support aliases. You can add an alias to an option by calling AddAlias
:
var option = new Option("--framework");
option.AddAlias("-f");
Given this alias, the following command lines are equivalent:
myapp -f net6.0
myapp --framework net6.0
Command aliases work the same way.
var command = new Command("serialize");
command.AddAlias("serialise");
This code makes the following command lines equivalent:
myapp serialize
myapp serialise
We recommend that you minimize the number of option aliases that you define, and avoid defining certain aliases in particular. For more information, see Short-form aliases.
Required options
To make an option required, set its IsRequired
property to true
, as shown in the following example:
var endpointOption = new Option<Uri>("--endpoint") { IsRequired = true };
var command = new RootCommand();
command.Add(endpointOption);
command.SetHandler((uri) =>
{
Console.WriteLine(uri?.GetType());
Console.WriteLine(uri?.ToString());
},
endpointOption);
await command.InvokeAsync(args);
The options section of the command help indicates the option is required:
Options:
--endpoint <uri> (REQUIRED)
--version Show version information
-?, -h, --help Show help and usage information
If the command line for this example app doesn't include --endpoint
, an error message is displayed and the command handler isn't called:
Option '--endpoint' is required.
If a required option has a default value, the option doesn't have to be specified on the command line. In that case, the default value provides the required option value.
Hidden commands, options, and arguments
You might want to support a command, option, or argument, but avoid making it easy to discover. For example, it might be a deprecated or administrative or preview feature. Use the IsHidden property to prevent users from discovering such features by using tab completion or help, as shown in the following example:
var endpointOption = new Option<Uri>("--endpoint") { IsHidden = true };
var command = new RootCommand();
command.Add(endpointOption);
command.SetHandler((uri) =>
{
Console.WriteLine(uri?.GetType());
Console.WriteLine(uri?.ToString());
},
endpointOption);
await command.InvokeAsync(args);
The options section of this example's command help omits the --endpoint
option.
Options:
--version Show version information
-?, -h, --help Show help and usage information
Set argument arity
You can explicitly set argument arity by using the Arity
property, but in most cases that is not necessary. System.CommandLine
automatically determines the argument arity based on the argument type:
Argument type | Default arity |
---|---|
Boolean |
ArgumentArity.ZeroOrOne |
Collection types | ArgumentArity.ZeroOrMore |
Everything else | ArgumentArity.ExactlyOne |
Multiple arguments
By default, when you call a command, you can repeat an option name to specify multiple arguments for an option that has maximum arity greater than one.
myapp --items one --items two --items three
To allow multiple arguments without repeating the option name, set Option.AllowMultipleArgumentsPerToken to true
. This setting lets you enter the following command line.
myapp --items one two three
The same setting has a different effect if maximum argument arity is 1. It allows you to repeat an option but takes only the last value on the line. In the following example, the value three
would be passed to the app.
myapp --item one --item two --item three
List valid argument values
To specify a list of valid values for an option or argument, specify an enum as the option type or use FromAmong, as shown in the following example:
var languageOption = new Option<string>(
"--language",
"An option that that must be one of the values of a static list.")
.FromAmong(
"csharp",
"fsharp",
"vb",
"pwsh",
"sql");
Here's an example of command-line input and the resulting output for the preceding example code:
myapp --language not-a-language
Argument 'not-a-language' not recognized. Must be one of:
'csharp'
'fsharp'
'vb'
'pwsh'
'sql'
The options section of command help shows the valid values:
Options:
--language <csharp|fsharp|vb|pwsh|sql> An option that must be one of the values of a static list.
--version Show version information
-?, -h, --help Show help and usage information
Option and argument validation
For information about argument validation and how to customize it, see the following sections in the Parameter binding article: