重要
System.CommandLine
目前为预览版,本文档适用于版本 2.0 beta 5。
某些信息与预发布产品有关,该产品在发布前可能会进行大幅修改。 Microsoft对此处提供的信息不作任何明示或暗示的保证。
System.CommandLine 提供命令行分析和作调用之间的明确分隔。 分析过程负责分析命令行输入,并创建 System.CommandLine.ParseResult
包含已分析值的对象(和分析错误)。 作调用过程负责调用与已分析的命令、选项或指令关联的作(参数不能有作)。
在本教程 入门 System.CommandLine 的以下示例中, ParseResult
通过分析命令行输入来创建该示例。 未定义或调用任何作:
using System.CommandLine;
using System.CommandLine.Parsing;
namespace scl;
class Program
{
static int Main(string[] args)
{
Option<FileInfo> fileOption = new("--file")
{
Description = "The file to read and display on the console."
};
RootCommand rootCommand = new("Sample app for System.CommandLine");
rootCommand.Options.Add(fileOption);
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
ReadFile(parsedFile);
return 0;
}
foreach (ParseError parseError in parseResult.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
return 1;
}
static void ReadFile(FileInfo file)
{
foreach (string line in File.ReadLines(file.FullName))
{
Console.WriteLine(line);
}
}
}
成功分析给定命令(或指令或选项)时,将调用作。 该作是一个委托,它采用参数 System.CommandLine.ParseResult
并返回 int
退出代码(异步作也 可用)。 退出代码由 System.CommandLine.Parsing.ParseResult.Invoke
该方法返回,可用于指示命令是否已成功执行。
在本教程 入门 System.CommandLine 的以下示例中,为根命令定义该作,并在分析命令行输入后调用:
using System.CommandLine;
namespace scl;
class Program
{
static int Main(string[] args)
{
Option<FileInfo> fileOption = new("--file")
{
Description = "The file to read and display on the console."
};
RootCommand rootCommand = new("Sample app for System.CommandLine");
rootCommand.Options.Add(fileOption);
rootCommand.SetAction(parseResult =>
{
FileInfo parsedFile = parseResult.GetValue(fileOption);
ReadFile(parsedFile);
return 0;
});
ParseResult parseResult = rootCommand.Parse(args);
return parseResult.Invoke();
}
static void ReadFile(FileInfo file)
{
foreach (string line in File.ReadLines(file.FullName))
{
Console.WriteLine(line);
}
}
}
某些内置符号(例如 System.CommandLine.Help.HelpOption
, System.CommandLine.VersionOption
或 System.CommandLine.Completions.SuggestDirective
)附带预定义的作。 这些符号会在创建根命令时自动添加到根命令,调用这些 System.CommandLine.Parsing.ParseResult
符号时,它们“只需工作”。使用作可以专注于应用逻辑,而库负责分析和调用内置符号的作。 如果愿意,可以坚持分析过程,而不定义任何作(如上面的第一个示例所示)。
ParseResult
该 System.CommandLine.Parsing.ParseResult
类型是一个类,表示分析命令行输入的结果。 你需要使用它来获取选项和参数的已分析值(无论是否使用作)。 还可以检查是否存在任何分析错误或不匹配 的令牌。
GetValue
此方法 System.CommandLine.Parsing.ParseResult.GetValue<T>
允许检索选项和参数的值:
int integer = parseResult.GetValue(delayOption);
string? message = parseResult.GetValue(messageOption);
还可以按名称获取值,但这要求指定要获取的值的类型。
以下示例使用 C# 集合初始值设定项创建根命令:
RootCommand rootCommand = new("Parameter binding example")
{
new Option<int>("--delay")
{
Description = "An option whose argument is parsed as an int."
},
new Option<string>("--message")
{
Description = "An option whose argument is parsed as a string."
}
};
然后,它使用 GetValue
该方法按名称获取值:
rootCommand.SetAction(parseResult =>
{
int integer = parseResult.GetValue<int>("--delay");
string? message = parseResult.GetValue<string>("--message");
DisplayIntAndString(integer, message);
});
此重载 GetValue
在分析的命令(而不是整个符号树)的上下文中获取指定符号名称的已分析或默认值。 它接受符号名称,而不是 别名。
分析错误
该 System.CommandLine.Parsing.ParseResult.Errors
属性包含分析过程中发生的分析错误列表。 每个错误都由一个 System.CommandLine.Parsing.ParseError
对象表示,该对象包含有关错误的信息,例如错误消息和导致错误的令牌。
调用 System.CommandLine.Parsing.ParseResult.Invoke
该方法时,它将返回一个退出代码,该代码指示分析是否成功。 如果存在任何分析错误,则退出代码为非零,所有分析错误都输出到标准错误。
如果不调用 System.CommandLine.Parsing.ParseResult.Invoke
该方法,则需要通过打印它们自行处理错误:
foreach (ParseError parseError in parseResult.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
return 1;
不匹配的令牌
该 System.CommandLine.Parsing.ParseResult.UnmatchedTokens
属性包含已分析但与任何配置的命令、选项或参数不匹配的令牌列表。
不匹配的令牌列表在行为类似于包装器的命令中非常有用。 包装器命令采用一组 令牌 ,并将其转发到另一个命令或应用。
sudo
Linux 中的命令是一个示例。 它采用用户的名称来模拟后跟要运行的命令。 例如:
sudo -u admin apt update
此命令行将作为用户admin
运行apt update
命令。
若要实现如下所示的包装器命令,请将命令属性 System.CommandLine.Command.TreatUnmatchedTokensAsErrors
设置为 false
。 然后,该 System.CommandLine.Parsing.ParseResult.UnmatchedTokens
属性将包含不显式属于该命令的所有参数。 在前面的示例中,ParseResult.UnmatchedTokens
将包含和apt
update
标记。
行动
作是成功分析命令(或选项或指令)时调用的委托。 它们采用参数 System.CommandLine.ParseResult
并返回 int
(或 Task<int>
) 退出代码。 退出代码用于指示作是否已成功执行。
System.CommandLine 提供抽象基类 System.CommandLine.CommandLineAction
和两个派生类: System.CommandLine.SynchronousCommandLineAction
和 System.CommandLine.AsynchronousCommandLineAction
。 前者用于返回 int
退出代码的同步作,而后者用于返回退出代码的 Task<int>
异步作。
无需创建派生类型来定义作。 可以使用该方法 System.CommandLine.Command.SetAction
为命令设置作。 同步作可以是采用 System.CommandLine.ParseResult
参数并返回退出代码的 int
委托。 异步作可以是接受参数System.CommandLine.ParseResult
并CancellationToken返回 a.Task<int>
rootCommand.SetAction(parseResult =>
{
FileInfo parsedFile = parseResult.GetValue(fileOption);
ReadFile(parsedFile);
return 0;
});
异步作
同步和异步作不应在同一应用程序中混合。 如果要使用异步作,应用程序需要从上到下异步。 这意味着所有作都应是异步的,应使用 System.CommandLine.Command.SetAction
接受返回 Task<int>
退出代码的委托的方法。 此外, CancellationToken 传递给作委托的作委托需要进一步传递给可取消的所有方法,例如文件 I/O作或网络请求。
除此之外,需要确保 System.CommandLine.Parsing.ParseResult.InvokeAsync
使用该方法而不是 System.CommandLine.Parsing.ParseResult.Invoke
使用此方法。 此方法是异步的,并返回 Task<int>
退出代码。 它还接受可用于取消作的可选 CancellationToken 参数。
前面的代码使用一个 SetAction
重载来获取 ParseResult 和一个 CancellationToken 而不仅仅是 ParseResult
:
static Task<int> Main(string[] args)
{
Option<string> urlOption = new("--url", "A URL.");
RootCommand rootCommand = new("Handle termination example") { urlOption };
rootCommand.SetAction((ParseResult parseResult, CancellationToken cancellationToken) =>
{
string? urlOptionValue = parseResult.GetValue(urlOption);
return DoRootCommand(urlOptionValue, cancellationToken);
});
return rootCommand.Parse(args).InvokeAsync();
}
public static async Task<int> DoRootCommand(
string? urlOptionValue, CancellationToken cancellationToken)
{
using HttpClient httpClient = new();
try
{
await httpClient.GetAsync(urlOptionValue, cancellationToken);
return 0;
}
catch (OperationCanceledException)
{
await Console.Error.WriteLineAsync("The operation was aborted");
return 1;
}
}
进程终止超时
System.CommandLine.CommandLineConfiguration.ProcessTerminationTimeout
通过调用期间传递给每个异步作的信号和处理进程终止(Ctrl+C,SIGINT
) SIGTERM
CancellationToken 启用信号处理。 它默认处于启用状态(2 秒),但你可以将其设置为 null
禁用它。
启用后,如果作未在指定的超时范围内完成,则进程将终止。 这可用于正常处理终止,例如,通过在进程终止之前保存状态。
若要测试上一段的示例代码,请使用 URL 运行命令,该 URL 需要一些时间才能加载完毕,然后在加载完成后按 Ctrl+C。 在 macOS 上按 Command+Period(.)。 例如:
testapp --url https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
The operation was aborted
退出代码
退出代码是由指示其成功或失败的作返回的整数值。 按照约定,退出代码 0
表示成功,而任何非零值都表示错误。 请务必在应用程序中定义有意义的退出代码,以便清楚地传达命令执行的状态。
每个 SetAction
方法都有一个重载,该重载接受返回 int
退出代码的委托,其中需要以显式方式提供退出代码和返回的 0
重载。
static int Main(string[] args)
{
Option<int> delayOption = new("--delay");
Option<string> messageOption = new("--message");
RootCommand rootCommand = new("Parameter binding example")
{
delayOption,
messageOption
};
rootCommand.SetAction(parseResult =>
{
Console.WriteLine($"--delay = {parseResult.GetValue(delayOption)}");
Console.WriteLine($"--message = {parseResult.GetValue(messageOption)}");
// Value returned from the action delegate is the exit code.
return 100;
});
return rootCommand.Parse(args).Invoke();
}