Partager via


Didacticiel : prise en main de System.CommandLine

Important

System.CommandLine est actuellement une PRÉVERSION et cette documentation concerne la version 2.0 beta 4. Certaines informations portent sur la préversion du produit qui est susceptible d’être en grande partie modifiée avant sa publication. Microsoft exclut toute garantie, expresse ou implicite, concernant les informations fournies ici.

Ce didacticiel montre comment créer une application en ligne de commande .NET qui utilise la bibliothèque System.CommandLine. Vous allez commencer par créer une commande racine simple avec une option. Ensuite, vous allez l’ajouter à cette base, ce qui crée une application plus complexe contenant plusieurs sous-commandes et différentes options pour chaque commande.

Dans ce tutoriel, vous allez apprendre à :

  • Créez des commandes, des options et des arguments.
  • Spécifiez les valeurs par défaut pour les options.
  • Attribuez des options et des arguments aux commandes.
  • Attribuez une option de façon récursive à toutes les sous-commandes d’une commande.
  • Utilisez plusieurs niveaux de sous-commandes imbriquées.
  • Créez des alias pour les commandes et les options.
  • Utilisez les types d’options string, string[], int, bool, FileInfo et énumération.
  • Liez les valeurs d’option au code du gestionnaire de commandes.
  • Utilisez du code personnalisé pour l’analyse et la validation des options.

Prérequis

ou

Créer l’application

Créez un projet d’application console .NET 6 nommé « scl ».

  1. Créez un dossier nommé scl pour le projet, puis ouvrez une invite de commandes dans le nouveau dossier.

  2. Exécutez la commande suivante :

    dotnet new console --framework net6.0
    

Installez le package System.CommandLine

  • Exécutez la commande suivante :

    dotnet add package System.CommandLine --prerelease
    

    L’option --prerelease est nécessaire, car la bibliothèque est toujours en version bêta.

  1. Remplacez le contenu du fichier Program.cs par le code suivant :

    using System.CommandLine;
    
    namespace scl;
    
    class Program
    {
        static async Task<int> Main(string[] args)
        {
            var fileOption = new Option<FileInfo?>(
                name: "--file",
                description: "The file to read and display on the console.");
    
            var rootCommand = new RootCommand("Sample app for System.CommandLine");
            rootCommand.AddOption(fileOption);
    
            rootCommand.SetHandler((file) => 
                { 
                    ReadFile(file!); 
                },
                fileOption);
    
            return await rootCommand.InvokeAsync(args);
        }
    
        static void ReadFile(FileInfo file)
        {
            File.ReadLines(file.FullName).ToList()
                .ForEach(line => Console.WriteLine(line));
        }
    }
    

Le code précédent :

  • Crée une option nommée --file de type FileInfo et l’affecte à la commande racine :

    var fileOption = new Option<FileInfo?>(
        name: "--file",
        description: "The file to read and display on the console.");
    
    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    rootCommand.AddOption(fileOption);
    
  • Spécifie que ReadFile est la méthode qui sera appelée lorsque la commande racine est appelée :

    rootCommand.SetHandler((file) => 
        { 
            ReadFile(file!); 
        },
        fileOption);
    
  • Affiche le contenu du fichier spécifié lorsque la commande racine est appelée :

    static void ReadFile(FileInfo file)
    {
        File.ReadLines(file.FullName).ToList()
            .ForEach(line => Console.WriteLine(line));
    }
    

Test de l'application

Vous pouvez utiliser l’une des méthodes suivantes pour tester lors du développement d’une application en ligne de commande :

  • Exécutez la commande dotnet build, puis ouvrez une invite de commandes dans le dossier scl/bin/Debug/net6.0 pour exécuter le fichier exécutable :

    dotnet build
    cd bin/Debug/net6.0
    scl --file scl.runtimeconfig.json
    
  • Utilisez dotnet run et passez des valeurs d’option à l’application plutôt qu’à la commande run en les incluant après --, comme dans l’exemple suivant :

    dotnet run -- --file scl.runtimeconfig.json
    

    Dans la préversion du SDK .NET 7.0.100, vous pouvez utiliser le commandLineArgs d’un fichier launchSettings.json en exécutant la commande dotnet run --launch-profile <profilename>.

  • Publiez le projet dans un dossier, ouvrez une invite de commandes dans ce dossier et exécutez le fichier exécutable :

    dotnet publish -o publish
    cd ./publish
    scl --file scl.runtimeconfig.json
    
  • Dans Visual Studio 2022, sélectionnez Debug>Debug Properties dans le menu, puis entrez les options et les arguments dans la zone Arguments de ligne de commande. Par exemple :

    Arguments de ligne de commande dans Visual Studio 2022

    Exécutez ensuite l’application en appuyant sur Ctrl+F5, par exemple.

Ce didacticiel part du principe que vous utilisez la première option parmi les options proposées.

Lorsque vous exécutez l’application, elle affiche le contenu du fichier spécifié par l’option --file.

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}

Résultat de l’aide

System.CommandLine fournit automatiquement un résultat de l’aide :

scl --help
Description:
  Sample app for System.CommandLine

Usage:
  scl [options]

Options:
  --file <file>   The file to read and display on the console.
  --version       Show version information
  -?, -h, --help  Show help and usage information

Sortie de version

System.CommandLine fournit automatiquement une sortie de version :

scl --version
1.0.0

Ajouter une sous-commande et des options

Dans cette section, vous allez :

  • Créez plus d’options.
  • Créez une sous-commande.
  • Attribuez les nouvelles options à la nouvelle sous-commande.

Les nouvelles options vous permettent de configurer les couleurs du texte de premier plan et d’arrière-plan, ainsi que la vitesse de lecture. Ces fonctionnalités seront utilisées pour lire une collection de guillemets provenant du didacticiel sur l’application console Teleprompter.

  1. Copiez le fichier sampleQuotes.txt à partir du dépôt GitHub pour cet exemple dans votre répertoire de projet. Pour plus d’informations sur le téléchargement de fichiers, consultez les instructions dans Exemples et didacticiels.

  2. Ouvrez le fichier projet et ajoutez un élément <ItemGroup>juste avant la balise de fermeture </Project> :

    <ItemGroup>
      <Content Include="sampleQuotes.txt">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    

    L’ajout de cette balise entraîne la copie du fichier texte dans le dossier bin/debug/net6.0 lorsque vous générez l’application. Ainsi, lorsque vous exécutez le fichier exécutable dans ce dossier, vous pouvez accéder au fichier par son nom sans spécifier de chemin d’accès au dossier.

  3. Dans Program.cs, après le code qui crée l’option --file, créez des options pour contrôler la vitesse de lecture et les couleurs du texte :

    var delayOption = new Option<int>(
        name: "--delay",
        description: "Delay between lines, specified as milliseconds per character in a line.",
        getDefaultValue: () => 42);
    
    var fgcolorOption = new Option<ConsoleColor>(
        name: "--fgcolor",
        description: "Foreground color of text displayed on the console.",
        getDefaultValue: () => ConsoleColor.White);
    
    var lightModeOption = new Option<bool>(
        name: "--light-mode",
        description: "Background color of text displayed on the console: default is black, light mode is white.");
    
  4. Après la ligne qui a créé la commande racine, supprimez la ligne qui y ajoute l’option --file. Vous la supprimez ici, car vous allez l’ajouter à une nouvelle sous-commande.

    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    //rootCommand.AddOption(fileOption);
    
  5. Après la ligne qui crée la commande racine, créez une sous-commande read. Ajoutez les options à cette sous-commande, puis ajoutez la sous-commande à la commande racine.

    var readCommand = new Command("read", "Read and display the file.")
        {
            fileOption,
            delayOption,
            fgcolorOption,
            lightModeOption
        };
    rootCommand.AddCommand(readCommand);
    
  6. Remplacez le code SetHandler par le code SetHandler suivant pour la nouvelle sous-commande :

    readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
        {
            await ReadFile(file!, delay, fgcolor, lightMode);
        },
        fileOption, delayOption, fgcolorOption, lightModeOption);
    

    Vous n’appelez plus SetHandler sur la commande racine, car la commande racine n’a plus besoin d’un gestionnaire. Lorsqu’une commande comporte des sous-commandes, vous devez généralement spécifier l’une des sous-commandes lors de l’appel d’une application en ligne de commande.

  7. Remplacez la méthode de gestionnaire ReadFile par le code suivant :

    internal static async Task ReadFile(
            FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        List<string> lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };
    }
    

L’application se présente désormais ainsi :

using System.CommandLine;

namespace scl;

class Program
{
    static async Task<int> Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
            name: "--file",
            description: "The file to read and display on the console.");

        var delayOption = new Option<int>(
            name: "--delay",
            description: "Delay between lines, specified as milliseconds per character in a line.",
            getDefaultValue: () => 42);

        var fgcolorOption = new Option<ConsoleColor>(
            name: "--fgcolor",
            description: "Foreground color of text displayed on the console.",
            getDefaultValue: () => ConsoleColor.White);

        var lightModeOption = new Option<bool>(
            name: "--light-mode",
            description: "Background color of text displayed on the console: default is black, light mode is white.");

        var rootCommand = new RootCommand("Sample app for System.CommandLine");
        //rootCommand.AddOption(fileOption);

        var readCommand = new Command("read", "Read and display the file.")
            {
                fileOption,
                delayOption,
                fgcolorOption,
                lightModeOption
            };
        rootCommand.AddCommand(readCommand);

        readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
            {
                await ReadFile(file!, delay, fgcolor, lightMode);
            },
            fileOption, delayOption, fgcolorOption, lightModeOption);

        return rootCommand.InvokeAsync(args).Result;
    }

    internal static async Task ReadFile(
            FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        List<string> lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };
    }
}

Tester la nouvelle sous-commande

Si vous essayez maintenant d’exécuter l’application sans spécifier la sous-commande, vous obtenez un message d’erreur suivi d’un message d’aide qui spécifie la sous-commande disponible.

scl --file sampleQuotes.txt
'--file' was not matched. Did you mean one of the following?
--help
Required command was not provided.
Unrecognized command or argument '--file'.
Unrecognized command or argument 'sampleQuotes.txt'.

Description:
  Sample app for System.CommandLine

Usage:
  scl [command] [options]

Options:
  --version       Show version information
  -?, -h, --help  Show help and usage information

Commands:
  read  Read and display the file.

Le texte d’aide de la sous-commande read indique que quatre options sont disponibles. Il affiche des valeurs valides pour l’énumération.

scl read -h
Description:
  Read and display the file.

Usage:
  scl read [options]

Options:
  --file <file>                                               The file to read and display on the console.
  --delay <delay>                                             Delay between lines, specified as milliseconds per
                                                              character in a line. [default: 42]
  --fgcolor                                                   Foreground color of text displayed on the console.
  <Black|Blue|Cyan|DarkBlue|DarkCyan|DarkGray|DarkGreen|Dark  [default: White]
  Magenta|DarkRed|DarkYellow|Gray|Green|Magenta|Red|White|Ye
  llow>
  --light-mode                                                Background color of text displayed on the console:
                                                              default is black, light mode is white.
  -?, -h, --help                                              Show help and usage information

Exécutez la sous-commande read en spécifiant uniquement l’option --file et vous obtenez les valeurs par défaut pour les trois autres options.

scl read --file sampleQuotes.txt

Le délai par défaut de 42 millisecondes par caractère entraîne une vitesse de lecture lente. Vous pouvez l’accélérer en définissant --delay sur un nombre inférieur.

scl read --file sampleQuotes.txt --delay 0

Vous pouvez utiliser --fgcolor et --light-mode pour définir les couleurs du texte :

scl read --file sampleQuotes.txt --fgcolor red --light-mode

Indiquez une valeur non valide pour --delay et vous obtiendrez un message d’erreur :

scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.

Indiquez une valeur non valide pour --file et vous obtiendrez une exception :

scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException:
Could not find file 'C:\bin\Debug\net6.0\nofile'.

Ajouter des sous-commandes et une validation personnalisée

Cette section crée la version finale de l’application. Lorsque vous avez terminé, l’application dispose des commandes et options suivantes :

  • commande racine avec une option globale* nommée --file
    • Commande quotes
      • commande read avec des options nommées --delay, --fgcolor et --light-mode
      • commande add avec des arguments nommés quote et byline
      • commande delete avec l’option nommée --search-terms

* Une option globale est disponible pour la commande à laquelle elle est affectée et à toutes ses sous-commandes de façon récursive.

Voici un exemple d’entrée de ligne de commande qui appelle chacune des commandes disponibles avec ses options et arguments :

scl quotes read --file sampleQuotes.txt --delay 40 --fgcolor red --light-mode
scl quotes add "Hello world!" "Nancy Davolio"
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
  1. Dans Program.cs, remplacez le code qui crée l’option --file par le code suivant :

    var fileOption = new Option<FileInfo?>(
        name: "--file",
        description: "An option whose argument is parsed as a FileInfo",
        isDefault: true,
        parseArgument: result =>
        {
            if (result.Tokens.Count == 0)
            {
                return new FileInfo("sampleQuotes.txt");
    
            }
            string? filePath = result.Tokens.Single().Value;
            if (!File.Exists(filePath))
            {
                result.ErrorMessage = "File does not exist";
                return null;
            }
            else
            {
                return new FileInfo(filePath);
            }
        });
    

    Ce code utilise ParseArgument<T> pour fournir une analyse, une validation et une gestion des erreurs personnalisées.

    Sans ce code, les fichiers manquants sont signalés avec une exception et une trace de pile. Avec ce code, seul le message d’erreur spécifié s’affiche.

    Ce code spécifie également une valeur par défaut, c’est pourquoi il définit isDefault sur true. Si vous ne définissez pas isDefault sur true, le délégué parseArgument n’est pas appelé quand aucune entrée n’est fournie pour --file.

  2. Après le code qui crée lightModeOption, ajoutez des options et des arguments pour les commandes add et delete :

    var searchTermsOption = new Option<string[]>(
        name: "--search-terms",
        description: "Strings to search for when deleting entries.")
        { IsRequired = true, AllowMultipleArgumentsPerToken = true };
    
    var quoteArgument = new Argument<string>(
        name: "quote",
        description: "Text of quote.");
    
    var bylineArgument = new Argument<string>(
        name: "byline",
        description: "Byline of quote.");
    

    Le paramètre AllowMultipleArgumentsPerToken vous permet d’omettre le nom de l’option --search-terms lors de la spécification d’éléments dans la liste après le premier. Les exemples suivants d’entrée de ligne de commande sont équivalents :

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Remplacez le code qui crée la commande racine et la commande read par le code suivant :

    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    rootCommand.AddGlobalOption(fileOption);
    
    var quotesCommand = new Command("quotes", "Work with a file that contains quotes.");
    rootCommand.AddCommand(quotesCommand);
    
    var readCommand = new Command("read", "Read and display the file.")
        {
            delayOption,
            fgcolorOption,
            lightModeOption
        };
    quotesCommand.AddCommand(readCommand);
    
    var deleteCommand = new Command("delete", "Delete lines from the file.");
    deleteCommand.AddOption(searchTermsOption);
    quotesCommand.AddCommand(deleteCommand);
    
    var addCommand = new Command("add", "Add an entry to the file.");
    addCommand.AddArgument(quoteArgument);
    addCommand.AddArgument(bylineArgument);
    addCommand.AddAlias("insert");
    quotesCommand.AddCommand(addCommand);
    

    Ce code apporte les modifications suivantes :

    • Supprime l’option --file de la commande read.

    • Ajoute l’option --file en tant qu’option globale à la commande racine.

    • Crée une commande quotes et l’ajoute à la commande racine.

    • Ajoute la commande read à la commande quotes au lieu de l’ajouter à la commande racine.

    • Crée des commandes add et delete et les ajoute à la commande quotes.

    Le résultat est la hiérarchie de commandes suivante :

    • Commande racine
      • quotes
        • read
        • add
        • delete

    L’application implémente maintenant le modèle recommandé où la commande parente (quotes) spécifie une zone ou un groupe, et ses commandes enfants (read, add, delete) sont des actions.

    Les options globales sont appliquées à la commande et de manière récursive aux sous-commandes. Puisque --file se trouve sur la commande racine, elle est automatiquement disponible dans toutes les sous-commandes de l’application.

  4. Après le code SetHandler, ajoutez un nouveau code SetHandler pour les nouvelles sous-commandes :

    deleteCommand.SetHandler((file, searchTerms) =>
        {
            DeleteFromFile(file!, searchTerms);
        },
        fileOption, searchTermsOption);
    
    addCommand.SetHandler((file, quote, byline) =>
        {
            AddToFile(file!, quote, byline);
        },
        fileOption, quoteArgument, bylineArgument);
    

    La sous-commande quotes n’a pas de gestionnaire, car il ne s’agit pas d’une commande feuille. Les sous-commandes read, add et delete sont des commandes feuille sous quotes, et SetHandler est appelé pour chacune d’elles.

  5. Ajoutez les gestionnaires pour add et delete.

    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
        File.WriteAllLines(
            file.FullName, File.ReadLines(file.FullName)
                .Where(line => searchTerms.All(s => !line.Contains(s))).ToList());
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");
        using StreamWriter? writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
        writer.Flush();
    }
    

L’application terminée ressemble à ceci :

using System.CommandLine;

namespace scl;

class Program
{
    static async Task<int> Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
            name: "--file",
            description: "An option whose argument is parsed as a FileInfo",
            isDefault: true,
            parseArgument: result =>
            {
                if (result.Tokens.Count == 0)
                {
                    return new FileInfo("sampleQuotes.txt");

                }
                string? filePath = result.Tokens.Single().Value;
                if (!File.Exists(filePath))
                {
                    result.ErrorMessage = "File does not exist";
                    return null;
                }
                else
                {
                    return new FileInfo(filePath);
                }
            });

        var delayOption = new Option<int>(
            name: "--delay",
            description: "Delay between lines, specified as milliseconds per character in a line.",
            getDefaultValue: () => 42);

        var fgcolorOption = new Option<ConsoleColor>(
            name: "--fgcolor",
            description: "Foreground color of text displayed on the console.",
            getDefaultValue: () => ConsoleColor.White);

        var lightModeOption = new Option<bool>(
            name: "--light-mode",
            description: "Background color of text displayed on the console: default is black, light mode is white.");

        var searchTermsOption = new Option<string[]>(
            name: "--search-terms",
            description: "Strings to search for when deleting entries.")
            { IsRequired = true, AllowMultipleArgumentsPerToken = true };

        var quoteArgument = new Argument<string>(
            name: "quote",
            description: "Text of quote.");

        var bylineArgument = new Argument<string>(
            name: "byline",
            description: "Byline of quote.");

        var rootCommand = new RootCommand("Sample app for System.CommandLine");
        rootCommand.AddGlobalOption(fileOption);

        var quotesCommand = new Command("quotes", "Work with a file that contains quotes.");
        rootCommand.AddCommand(quotesCommand);

        var readCommand = new Command("read", "Read and display the file.")
            {
                delayOption,
                fgcolorOption,
                lightModeOption
            };
        quotesCommand.AddCommand(readCommand);

        var deleteCommand = new Command("delete", "Delete lines from the file.");
        deleteCommand.AddOption(searchTermsOption);
        quotesCommand.AddCommand(deleteCommand);

        var addCommand = new Command("add", "Add an entry to the file.");
        addCommand.AddArgument(quoteArgument);
        addCommand.AddArgument(bylineArgument);
        addCommand.AddAlias("insert");
        quotesCommand.AddCommand(addCommand);

        readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
            {
                await ReadFile(file!, delay, fgcolor, lightMode);
            },
            fileOption, delayOption, fgcolorOption, lightModeOption);

        deleteCommand.SetHandler((file, searchTerms) =>
            {
                DeleteFromFile(file!, searchTerms);
            },
            fileOption, searchTermsOption);

        addCommand.SetHandler((file, quote, byline) =>
            {
                AddToFile(file!, quote, byline);
            },
            fileOption, quoteArgument, bylineArgument);

        return await rootCommand.InvokeAsync(args);
    }

    internal static async Task ReadFile(
                FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        var lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };

    }
    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
        File.WriteAllLines(
            file.FullName, File.ReadLines(file.FullName)
                .Where(line => searchTerms.All(s => !line.Contains(s))).ToList());
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");
        using StreamWriter? writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
        writer.Flush();
    }
}

Générez le projet, puis essayez les commandes suivantes.

Envoyez un fichier inexistant à --file avec la commande read, et vous obtenez un message d’erreur au lieu d’une exception et d’une trace de pile :

scl quotes read --file nofile
File does not exist

Essayez d’exécuter la sous-commande quotes et vous obtenez un message vous indiquant d’utiliser read, add ou delete :

scl quotes
Required command was not provided.

Description:
  Work with a file that contains quotes.

Usage:
  scl quotes [command] [options]

Options:
  --file <file>   An option whose argument is parsed as a FileInfo [default: sampleQuotes.txt]
  -?, -h, --help  Show help and usage information

Commands:
  read                          Read and display the file.
  delete                        Delete lines from the file.
  add, insert <quote> <byline>  Add an entry to the file.

Exécutez la sous-commande add, puis examinez la fin du fichier texte pour voir le texte ajouté :

scl quotes add "Hello world!" "Nancy Davolio"

Exécutez la sous-commande delete avec des chaînes de recherche à partir du début du fichier, puis examinez le début du fichier texte pour voir où le texte a été supprimé :

scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"

Notes

Si vous exécutez dans le dossier bin/debug/net6.0, c’est dans ce dossier que vous trouverez le fichier avec les modifications des commandes add et delete. La copie du fichier dans le dossier du projet reste inchangée.

Étapes suivantes

Dans ce didacticiel, vous avez créé une application de ligne de commande simple qui utilise System.CommandLine. Pour en savoir plus sur la bibliothèque, consultez vue d’ensemble de System.CommandLine.