Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
System.CommandLine fornece uma separação clara entre análise de linha de comando e invocação de ação. O processo de análise é responsável por analisar a entrada da linha de comando e criar um ParseResult objeto que contém os valores analisados (e erros de análise). O processo de invocação de ação é responsável por invocar a ação associada ao comando, opção ou diretiva analisada (os argumentos não podem ter ações).
No exemplo a seguir do tutorial Introdução ao System.CommandLine tutorial, o ParseResult é criado analisando a entrada de linha de comando. Nenhuma ação é definida ou invocada:
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.Errors.Count == 0 && 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);
}
}
}
Uma ação é invocada quando um determinado comando (ou diretiva, ou opção) é analisado com êxito. A ação é um delegado que usa um ParseResult argumento e retorna um int código de saída (ações assíncronas também estão disponíveis). O código de saída é retornado pelo ParseResult.Invoke(InvocationConfiguration) método e pode ser usado para indicar se o comando foi executado com êxito ou não.
No exemplo a seguir do tutorial Introdução ao System.CommandLine tutorial, a ação é definida para o comando raiz e invocada após a análise da entrada da linha de comando:
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);
}
}
}
Alguns símbolos internos, como HelpOption, VersionOptione SuggestDirective, vêm com ações predefinidas. Esses símbolos são adicionados automaticamente ao comando root quando você o cria, e quando você invoca o ParseResult, eles "simplesmente funcionam". O uso de ações permite que você se concentre na lógica do aplicativo, enquanto a biblioteca cuida da análise e da invocação de ações para símbolos internos. Se preferires, podes seguir o processo de análise sintática e não definir nenhuma ação (como no primeiro exemplo deste artigo).
ParseResult
A ParseResult classe representa os resultados da análise da entrada de linha de comando. Você precisa usá-lo para obter os valores analisados para opções e argumentos (não importa se você está usando ações ou não). Você também pode verificar se houve erros de análise ou tokens incomparáveis.
GetValue
O ParseResult.GetValue método permite recuperar os valores de opções e argumentos:
int integer = parseResult.GetValue(delayOption);
string? message = parseResult.GetValue(messageOption);
Você também pode obter valores por nome, mas isso requer que você especifique o tipo do valor que deseja obter.
O exemplo a seguir usa inicializadores de coleção C# para criar um comando raiz:
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."
}
};
Em seguida, ele usa o GetValue método para obter os valores por nome:
rootCommand.SetAction(parseResult =>
{
int integer = parseResult.GetValue<int>("--delay");
string? message = parseResult.GetValue<string>("--message");
DisplayIntAndString(integer, message);
});
Essa sobrecarga de GetValue obtém o valor analisado ou padrão para o nome do símbolo especificado, no contexto do comando analisado (não na árvore de símbolos inteira). Ele aceita o nome do símbolo, não um alias.
Erros de análise
A ParseResult.Errors propriedade contém uma lista de erros de análise que ocorreram durante o processo de análise. Cada erro é representado por um ParseError objeto, que contém informações sobre o erro, como a mensagem de erro e o token que causou o erro.
Quando você chama o ParseResult.Invoke(InvocationConfiguration) método, ele retorna um código de saída que indica se a análise foi bem-sucedida ou não. Se houver algum erro de análise, o código de saída será diferente de zero e todos os erros de análise serão impressos no erro padrão.
Se você não chamar o ParseResult.Invoke método, você precisa lidar com os erros por conta própria, por exemplo, imprimindo-os:
foreach (ParseError parseError in parseResult.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
return 1;
Tokens que não correspondem
A UnmatchedTokens propriedade contém uma lista dos tokens que foram analisados, mas não corresponderam a nenhum comando, opção ou argumento configurado.
A lista de tokens não correspondentes é útil em comandos que se comportam como wrappers. Um comando wrapper pega um conjunto de tokens e os encaminha para outro comando ou aplicativo. O sudo comando no Linux é um exemplo. Requer o nome de um utilizador para personificar, seguido de um comando para executar. Por exemplo, o comando a seguir executa o apt update comando como o usuário admin:
sudo -u admin apt update
Para implementar um comando wrapper como este, defina a propriedade do comando System.CommandLine.Command.TreatUnmatchedTokensAsErrors para false. Em seguida, a System.CommandLine.Parsing.ParseResult.UnmatchedTokens propriedade conterá todos os argumentos que não pertencem explicitamente ao comando. No exemplo anterior, ParseResult.UnmatchedTokens conteria os apt tokens e update .
Ações
As ações são delegados que são acionados quando um comando (ou uma opção ou diretiva) é interpretado com sucesso. Eles pegam um ParseResult argumento e retornam um int (ou Task<int>) código de saída. O código de saída é usado para indicar se a ação foi executada com êxito ou não.
System.CommandLine fornece uma classe CommandLineAction base abstrata e duas classes derivadas: SynchronousCommandLineAction e AsynchronousCommandLineAction. O primeiro é usado para ações síncronas que retornam um int código de saída, enquanto o segundo é usado para ações assíncronas que retornam um código de Task<int> saída.
Não é necessário criar um tipo derivado para definir uma ação. Você pode usar o SetAction método para definir uma ação para um comando. A ação síncrona pode ser um delegado que usa um ParseResult argumento e retorna um int código de saída. A ação assíncrona pode ser um delegado que usa ParseResult e CancellationToken argumenta e retorna um Task<int>arquivo .
rootCommand.SetAction(parseResult =>
{
FileInfo parsedFile = parseResult.GetValue(fileOption);
ReadFile(parsedFile);
return 0;
});
Ações assíncronas
Ações síncronas e assíncronas não devem ser misturadas no mesmo aplicativo. Se você quiser usar ações assíncronas, seu aplicativo precisará ser assíncrono durante todo o processo. Isso significa que todas as ações devem ser assíncronas e você deve usar o SetAction método que aceita um delegado retornando um Task<int> código de saída. Além disso, o CancellationToken que é passado para o delegado de ação precisa ser passado para todos os métodos que podem ser cancelados, como operações de E/S de arquivo ou solicitações de rede.
Você também precisa garantir que o ParseResult.InvokeAsync(InvocationConfiguration, CancellationToken) método seja usado em vez de Invoke. Esse método é assíncrono e retorna um código de Task<int> saída. Ele também aceita um parâmetro opcional CancellationToken que pode ser usado para cancelar a ação.
O código a seguir usa uma SetAction sobrecarga que obtém um ParseResult e um CancellationToken em vez de apenas ParseResult:
static Task<int> Main(string[] args)
{
Option<string> urlOption = new("--url")
{
Description = "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;
}
}
Tempo limite de rescisão do processo
ProcessTerminationTimeout permite a sinalização e o tratamento do encerramento do processo (Ctrl+C, SIGINT, SIGTERM) através de um CancellationToken que é passado para cada ação assíncrona durante a invocação. Ele está ativado por padrão (2 segundos), mas você pode configurá-lo para null desativá-lo.
Quando habilitada, se a ação não for concluída dentro do tempo limite especificado, o processo será encerrado. Isso é útil para lidar com a rescisão normalmente, por exemplo, salvando o estado antes que o processo seja encerrado.
Para testar o código de exemplo do parágrafo anterior, execute o comando com uma URL que levará um momento para carregar e, antes de terminar de carregar, pressione Ctrl+C. No macOS, pressione Command+Period(.). Por exemplo:
testapp --url https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
The operation was aborted
Códigos de saída
O código de saída é um valor inteiro retornado por uma ação que indica seu sucesso ou falha. Por convenção, um código de saída 0 significa sucesso, enquanto qualquer valor diferente de zero indica um erro. É importante definir códigos de saída significativos em seu aplicativo para comunicar claramente o status da execução do comando.
Cada SetAction método tem uma sobrecarga que aceita um delegado retornando um int código de saída onde o código de saída precisa ser fornecido de forma explícita e uma sobrecarga que retorna 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();
}