Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
System.CommandLine fornece uma separação clara entre a análise de linha de comando e a 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 analisar erros). O processo de invocação de ação é responsável por invocar a ação associada ao comando analisado, à opção ou à diretiva (os argumentos não podem ter ações).
No exemplo a seguir do tutorial Introdução System.CommandLine , ele ParseResult é criado analisando a entrada da 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 System.CommandLine , a ação é definida para o comando raiz e invocada após analisar a 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 raiz quando você o cria e, quando você invoca, ParseResulteles "apenas 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 preferir, você pode manter o processo de análise 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 incompatí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 exige 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 o valor padrão para o nome do símbolo especificado, dentro do contexto do comando analisado (e não de toda a árvore de símbolos). Ele aceita o nome do símbolo, não um alias.
Erros de análise sintática
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 erros de análise, o código de saída não será 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, precisará 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 sem correspondência
A UnmatchedTokens propriedade contém uma lista dos tokens que foram analisados, mas não correspondeu a nenhum comando, opção ou argumento configurado.
A lista de tokens sem correspondência é útil em comandos que se comportam como encapsuladores. Um comando wrapper usa um conjunto de tokens e os encaminha para outro comando ou aplicativo. O sudo comando no Linux é um exemplo. É preciso o nome de um usuário para representar seguido por um comando para ser executado. 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, configure 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 tokens apt e update.
Ações
As ações são delegados invocados quando um comando (ou uma opção ou uma diretiva) é analisado 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 último é usado para ações assíncronas que retornam um Task<int> código de saída.
Você não precisa 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 aceita ParseResult e CancellationToken argumenta e retorna um Task<int>.
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 tempo. 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 ainda mais 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 Task<int> código de 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 em vez de CancellationToken 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 término do processo
ProcessTerminationTimeout permite a sinalização e o tratamento da terminação do processo (Ctrl+C, SIGINT, ) SIGTERMpor meio de uma CancellationToken que é passada para cada ação assíncrona durante a invocação. Ele está habilitado por padrão (2 segundos), mas você pode defini-lo para null desabilitá-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 o encerramento normalmente, por exemplo, salvando o estado antes do processo ser encerrado.
Para testar o código de exemplo do parágrafo anterior, execute o comando com uma URL que levará um momento para ser carregada e, antes de concluir o carregamento, 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 êxito ou falha. Por convenção, um código de saída de 0 significa êxito, 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 método SetAction tem uma sobrecarga que aceita um delegado retornando um código de saída int, 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();
}