如何在 System.CommandLine 中定义命令、选项和参数
重要
System.CommandLine
目前为预览版,本文档适用于版本 2.0 beta 4。
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
本文介绍如何在使用 System.CommandLine
库构建的命令行应用中定义命令、选项和参数。 若要生成说明这些方法的完整应用程序,请参阅教程 System.CommandLine 入门。
有关如何设计命令行应用的命令、选项和参数的指南,请参阅设计指南。
定义根命令
每个命令行应用都有一个根命令,该命令引用可执行文件本身。 如果应用没有子命令、选项或参数,则调用代码的最简单情况如下所示:
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);
}
}
定义子命令
命令可以具有子命令(称为子命令或谓词),并且它们可以根据你的需要嵌套任意多个级别。 可以添加到子命令,如下面的示例所示:
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);
可以调用此示例中最内部的子命令,如下所示:
myapp sub1 sub1a
定义选项
命令处理程序方法通常具有参数,并且值可能来自命令行选项。 以下示例创建两个选项,并将其添加到根命令中。 选项名称包括双连字符前缀,这是 POSIX CLIS 的典型特征。 命令处理程序代码显示这些选项的值:
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);
下面是命令行输入示例,以及上述示例代码的结果输出:
myapp --delay 21 --message "Hello world!"
--delay = 21
--message = Hello world!
全局选项
若要一次向一个命令添加选项,请使用 Add
或 AddOption
方法,如前面的示例所示。 若要向命令添加选项并以递归方式添加到它的所有子命令,请使用 AddGlobalOption
方法,如以下示例所示:
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);
前面的代码将 --delay
添加为根命令的全局选项,并且它在 subCommand1a
的处理程序中可用。
定义参数
定义参数并将其添加到选项等命令中。 下面的示例与选项示例类似,但它定义参数而不是选项:
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);
下面是命令行输入示例,以及上述示例代码的结果输出:
myapp 42 "Hello world!"
<delay> argument = 42
<message> argument = Hello world!
未定义默认值的参数(如前面示例中所示的 messageArgument
)被视为必需参数。 如果未提供所需的参数,则会显示一条错误消息,并且不会调用命令处理程序。
定义别名
命令和选项都支持别名。 可以通过调用 AddAlias
将别名添加到选项:
var option = new Option("--framework");
option.AddAlias("-f");
鉴于此别名,以下命令行是等效的:
myapp -f net6.0
myapp --framework net6.0
命令别名的运行方式相同。
var command = new Command("serialize");
command.AddAlias("serialise");
此代码使以下命令行等效:
myapp serialize
myapp serialise
建议尽量减少定义的选项别名数,并避免特别定义某些别名。 有关详细信息,请参阅缩写形式别名。
必需选项
若要使选项成为必需选项,请将其 IsRequired
设置为 true
,如以下示例所示:
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);
命令帮助的 options 部分指示该选项是必需的:
Options:
--endpoint <uri> (REQUIRED)
--version Show version information
-?, -h, --help Show help and usage information
如果此示例应用的命令行不包含 --endpoint
,则会显示错误消息,并且不会调用命令处理程序:
Option '--endpoint' is required.
如果必需选项具有默认值,则无需在命令行上指定该选项。 在这种情况下,默认值将提供所需的选项值。
隐藏命令、选项和参数
你可能想要支持命令、选项或参数,但希望避免使其易于发现。 例如,它可能是已弃用的、管理或预览功能。 使用 IsHidden 属性通过 Tab 自动补全或帮助来防止用户发现此类功能,如以下示例所示:
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);
此示例命令的 options 部分有助于省略 --endpoint
选项。
Options:
--version Show version information
-?, -h, --help Show help and usage information
设置参数 arity
你可以使用 Arity
属性显式设置参数 arity,不过在大多数情况下这不是必需的。 System.CommandLine
根据参数类型自动确定参数 arity:
参数类型 | 默认 arity |
---|---|
Boolean |
ArgumentArity.ZeroOrOne |
集合类型 | ArgumentArity.ZeroOrMore |
其他 | ArgumentArity.ExactlyOne |
多个参数
默认情况下,调用命令时,可以重复选项名称,为最大 arity 大于 1 的选项指定多个参数。
myapp --items one --items two --items three
若要在不重复选项名称的情况下允许多个参数,请将 Option.AllowMultipleArgumentsPerToken 设置为 true
。 此设置允许你输入以下命令行。
myapp --items one two three
如果最大参数 arity 为 1,则同一设置具有不同的效果。 它允许重复一个选项,但仅采用行上的最后一个值。 在下面的示例中,值 three
将传递给应用。
myapp --item one --item two --item three
列出有效的参数值
若要为选项或参数指定有效值列表,请指定枚举作为选项类型或使用 FromAmong,如以下示例所示:
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");
下面是命令行输入示例,以及上述示例代码的结果输出:
myapp --language not-a-language
Argument 'not-a-language' not recognized. Must be one of:
'csharp'
'fsharp'
'vb'
'pwsh'
'sql'
命令帮助的 options 部分显示有效值:
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
选项和参数验证
有关参数验证以及如何自定义它的信息,请参阅参数绑定一文的以下部分: