Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
System.CommandLine fournit une séparation claire entre l’analyse de ligne de commande et l’appel d’action. Le processus d’analyse est responsable de l’analyse de l’entrée de ligne de commande et de la création d’un ParseResult objet qui contient les valeurs analysées (et analyse des erreurs). Le processus d’appel d’action est responsable de l’appel de l’action associée à la commande, à l’option ou à la directive analysée (les arguments ne peuvent pas avoir d’actions).
Dans l’exemple suivant à partir du didacticiel Get started System.CommandLine , il ParseResult est créé en analysant l’entrée de ligne de commande. Aucune action n’est définie ou appelée :
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);
}
}
}
Une action est appelée lorsqu’une commande donnée (ou une directive ou une option) est analysée avec succès. L’action est un délégué qui prend un ParseResult argument et retourne un code de int sortie (les actions asynchrones sont également disponibles). Le code de sortie est retourné par la ParseResult.Invoke(InvocationConfiguration) méthode et peut être utilisé pour indiquer si la commande a été exécutée correctement ou non.
Dans l’exemple suivant à partir du didacticiel Prise en main System.CommandLine , l’action est définie pour la commande racine et appelée après l’analyse de l’entrée de ligne de commande :
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);
}
}
}
Certains symboles intégrés, tels que HelpOption, VersionOptionet SuggestDirective, sont fournis avec des actions prédéfinies. Ces symboles sont automatiquement ajoutés à la commande racine lorsque vous le créez, et lorsque vous appelez le ParseResult, ils « fonctionnent simplement ». L’utilisation d’actions vous permet de vous concentrer sur votre logique d’application, tandis que la bibliothèque s’occupe de l’analyse et de l’appel d’actions pour les symboles intégrés. Si vous préférez, vous pouvez coller au processus d’analyse et ne pas définir d’actions (comme dans le premier exemple de cet article).
ParseResult
La ParseResult classe représente les résultats de l’analyse de l’entrée de ligne de commande. Vous devez l’utiliser pour obtenir les valeurs analysées pour les options et les arguments (peu importe si vous utilisez des actions ou non). Vous pouvez également vérifier s’il y avait des erreurs d’analyse ou des jetons sans correspondance.
GetValue
La ParseResult.GetValue méthode vous permet de récupérer les valeurs des options et des arguments :
int integer = parseResult.GetValue(delayOption);
string? message = parseResult.GetValue(messageOption);
Vous pouvez également obtenir des valeurs par nom, mais cela vous oblige à spécifier le type de la valeur que vous souhaitez obtenir.
L’exemple suivant utilise des initialiseurs de collection C# pour créer une commande racine :
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."
}
};
Ensuite, il utilise la GetValue méthode pour obtenir les valeurs par nom :
rootCommand.SetAction(parseResult =>
{
int integer = parseResult.GetValue<int>("--delay");
string? message = parseResult.GetValue<string>("--message");
DisplayIntAndString(integer, message);
});
Cette surcharge de GetValue obtient la valeur analysée ou par défaut pour le nom de symbole spécifié, dans le contexte de la commande analysée (et non de l’arborescence de symboles entière). Il accepte le nom du symbole, et non un alias.
Erreurs d’analyse
La ParseResult.Errors propriété contient une liste d’erreurs d’analyse qui se sont produites pendant le processus d’analyse. Chaque erreur est représentée par un ParseError objet, qui contient des informations sur l’erreur, telles que le message d’erreur et le jeton qui a provoqué l’erreur.
Lorsque vous appelez la ParseResult.Invoke(InvocationConfiguration) méthode, elle retourne un code de sortie qui indique si l’analyse a réussi ou non. S’il y avait des erreurs d’analyse, le code de sortie n’est pas égal à zéro et toutes les erreurs d’analyse sont imprimées dans l’erreur standard.
Si vous n’appelez pas la ParseResult.Invoke méthode, vous devez gérer les erreurs par exemple en les imprimant :
foreach (ParseError parseError in parseResult.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
return 1;
Jetons sans correspondance
La UnmatchedTokens propriété contient une liste des jetons analysés, mais qui ne correspondent à aucune commande, option ou argument configuré.
La liste des jetons sans correspondance est utile dans les commandes qui se comportent comme des wrappers. Une commande wrapper prend un ensemble de jetons et les transfère à une autre commande ou application. La sudo commande dans Linux est un exemple. Il utilise le nom d’un utilisateur pour usurper l’identité, suivi d’une commande à exécuter. Par exemple, la commande suivante exécute la apt update commande en tant qu’utilisateur admin:
sudo -u admin apt update
Pour implémenter une commande de contournement comme celle-ci, définissez la propriété de commande System.CommandLine.Command.TreatUnmatchedTokensAsErrors à false. Ensuite, la System.CommandLine.Parsing.ParseResult.UnmatchedTokens propriété contient tous les arguments qui n’appartiennent pas explicitement à la commande. Dans l’exemple précédent, ParseResult.UnmatchedTokens contiendrait les jetons apt et update.
Actions
Les actions sont des procédures ou fonctions déléguées qui sont appelées lorsqu’une commande (ou une option ou une directive) est interprétée avec succès. Ils prennent un ParseResult argument et retournent un code de int sortie (ou Task<int>) . Le code de sortie est utilisé pour indiquer si l’action a été exécutée correctement ou non.
System.CommandLine fournit une classe CommandLineAction de base abstraite et deux classes dérivées : SynchronousCommandLineAction et AsynchronousCommandLineAction. L’ancien est utilisé pour les actions synchrones qui retournent un code de int sortie, tandis que celui-ci est utilisé pour les actions asynchrones qui retournent un Task<int> code de sortie.
Vous n’avez pas besoin de créer un type dérivé pour définir une action. Vous pouvez utiliser la SetAction méthode pour définir une action pour une commande. L’action synchrone peut être un délégué qui prend un ParseResult argument et retourne un code de int sortie. L’action asynchrone peut être un délégué qui prend ParseResult et CancellationToken arguments et retourne un Task<int>.
rootCommand.SetAction(parseResult =>
{
FileInfo parsedFile = parseResult.GetValue(fileOption);
ReadFile(parsedFile);
return 0;
});
Actions asynchrones
Les actions synchrones et asynchrones ne doivent pas être mélangées dans la même application. Si vous souhaitez utiliser des actions asynchrones, votre application doit être asynchrone dans l’ensemble. Cela signifie que toutes les actions doivent être asynchrones et que vous devez utiliser la SetAction méthode qui accepte un délégué retournant un code de Task<int> sortie. De plus, le CancellationToken délégué d’action doit être transmis à toutes les méthodes qui peuvent être annulées, telles que les opérations d’E/S de fichier ou les demandes réseau.
Vous devez également vous assurer que la ParseResult.InvokeAsync(InvocationConfiguration, CancellationToken) méthode est utilisée au lieu de Invoke. Cette méthode est asynchrone et retourne un Task<int> code de sortie. Il accepte également un paramètre facultatif CancellationToken qui peut être utilisé pour annuler l’action.
Le code suivant utilise une surcharge qui obtient un SetActionParseResult et un CancellationToken plutôt que simplement 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;
}
}
Délai d'expiration pour l'arrêt du processus
ProcessTerminationTimeout active la signalisation et la gestion de l’arrêt du processus (Ctrl+C, SIGINT, SIGTERM) via une CancellationToken action asynchrone transmise à chaque action asynchrone pendant l’appel. Elle est activée par défaut (2 secondes), mais vous pouvez la définir pour null la désactiver.
Lorsqu’elle est activée, si l’action ne se termine pas dans le délai d’expiration spécifié, le processus est arrêté. Cela est utile pour gérer la terminaison de manière élégante, par exemple en sauvegardant l’état avant la fin du processus.
Pour tester l’exemple de code du paragraphe précédent, exécutez la commande avec une URL qui prendra un certain temps pour charger, puis avant de terminer le chargement, appuyez sur Ctrl+C. Sur macOS, appuyez sur Commande+Point (.). Par exemple:
testapp --url https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
The operation was aborted
Codes de sortie
Le code de sortie est une valeur entière retournée par une action indiquant sa réussite ou son échec. Par convention, un code de sortie de 0 indique la réussite, tandis que toute valeur non nulle indique une erreur. Il est important de définir des codes de sortie significatifs dans votre application pour communiquer clairement l’état de l’exécution des commandes.
Chaque SetAction méthode a une surcharge qui accepte un délégué retournant un int code de sortie où le code de sortie doit être fourni de manière explicite et une surcharge qui retourne 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();
}