如何在 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!

全局选项

若要一次向一个命令添加选项,请使用 AddAddOption 方法,如前面的示例所示。 若要向命令添加选项并以递归方式添加到它的所有子命令,请使用 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

选项和参数验证

有关参数验证以及如何自定义它的信息,请参阅参数绑定一文的以下部分:

请参阅