Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Importante
System.CommandLine
est actuellement disponible en préversion et cette documentation concerne la version 2.0 bêta 4.
Certaines informations se rapportent à un produit en version préliminaire qui peut être sensiblement modifié avant sa sortie. Microsoft n’offre aucune garantie, expresse ou implicite, en ce qui concerne les informations fournies ici.
Ce tutoriel montre comment créer une application en ligne de commande .NET qui utilise la System.CommandLine
bibliothèque. Vous commencerez par créer une commande racine simple qui a une option. Ensuite, vous allez ajouter à cette base, en créant une application plus complexe qui contient 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 à des commandes.
- Attribuez une option de manière récursive à toutes les sous-commandes sous une commande.
- Utilisez plusieurs niveaux de sous-commandes imbriqués.
- Créez des alias pour les commandes et les options.
- Travaillez avec
string
,string[]
,int
,bool
,FileInfo
et des types d'options enum. - Lier des valeurs d’option au code du gestionnaire de commandes.
- Utilisez du code personnalisé pour l’analyse et la validation des options.
Conditions préalables
- La dernière version du SDK .NET
- Éditeur de code Visual Studio
- Le DevKit C#
ou
- Visual Studio 2022 avec la charge de travail de développement de bureau .NET installée.
Créer l’application
Créez un projet d’application console .NET 6 nommé « scl ».
Créez un dossier nommé scl pour le projet, puis ouvrez une invite de commandes dans le nouveau dossier.
Exécutez la commande suivante:
dotnet new console --framework net6.0
Installer le package System.CommandLine
Exécutez la commande suivante:
dotnet package add System.CommandLine --prerelease
L’option
--prerelease
est nécessaire, car la bibliothèque est toujours en version bêta.
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)); } }
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 qu’il
ReadFile
s’agit de 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)); }
Tester l’application
Vous pouvez utiliser l’une des méthodes suivantes pour tester tout en développant une application en ligne de commande :
Exécutez la
dotnet build
commande, puis ouvrez une invite de commandes dans le dossier scl/bin/Debug/net6.0 pour exécuter l’exécutable :dotnet build cd bin/Debug/net6.0 scl --file scl.runtimeconfig.json
Utilisez
dotnet run
et transmettez des valeurs d’option à l’application au lieu de larun
commande en les incluant après--
, comme dans l’exemple suivant :dotnet run -- --file bin/Debug/net6.0/scl.runtimeconfig.json
Le répertoire de travail est le dossier du projet (le dossier qui contient le fichier .csproj), donc le chemin relatif à scl.runtimeconfig.json
part du dossier du projet.
Dans .NET 7.0.100 SDK Preview, vous pouvez utiliser le commandLineArgs
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 l’exécutable :
dotnet publish -o publish cd ./publish scl --file scl.runtimeconfig.json
Dans Visual Studio 2022, sélectionnez Déboguer>Propriétés de débogage dans le menu, puis entrez les options et les arguments dans la zone Arguments de ligne de commande. Par exemple:
Exécutez ensuite l’application, par exemple en appuyant sur Ctrl+F5.
Ce tutoriel suppose que vous utilisez la première de ces options.
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"
}
}
}
Sortie de l'aide
System.CommandLine
fournit automatiquement une sortie d’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 la version
System.CommandLine
fournit automatiquement la sortie de version :
scl --version
1.0.0
Ajouter une sous-commande et des options
Dans cette section, vous allez :
- Créez d’autres options.
- Créez une sous-commande.
- Affectez les nouvelles options à la nouvelle sous-commande.
Les nouvelles options vous permettent de configurer les couleurs du premier plan et du texte d’arrière-plan et la vitesse de lecture. Ces fonctionnalités seront utilisées pour lire une collection de citations tirées du didacticiel sur l’application console Teleprompter.
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 des exemples et des didacticiels.
Ouvrez le fichier projet et ajoutez un
<ItemGroup>
élément juste avant la balise de fermeture</Project>
:<ItemGroup> <Content Include="sampleQuotes.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
L’ajout de ce balisage entraîne la copie du fichier texte dans le dossier bin/debug/net6.0 lorsque vous générez l’application. Par conséquent, lorsque vous exécutez l’exécutable dans ce dossier, vous pouvez accéder au fichier par nom sans spécifier de chemin d’accès au dossier.
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.");
Après la ligne qui crée la commande racine, supprimez la ligne qui y ajoute l’option
--file
. Vous la supprimez ici, car vous l’ajouterez à une nouvelle sous-commande.var rootCommand = new RootCommand("Sample app for System.CommandLine"); //rootCommand.AddOption(fileOption);
Après la ligne qui crée la commande racine, créez une
read
sous-commande. Ajoutez les options à cette sous-commande et ajoutez la sous-commande à la commande racine.var readCommand = new Command("read", "Read and display the file.") { fileOption, delayOption, fgcolorOption, lightModeOption }; rootCommand.AddCommand(readCommand);
Remplacez le
SetHandler
code par le code suivantSetHandler
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
SetHandler
plus sur la commande racine, car la commande racine n’a plus besoin d’un gestionnaire. Lorsqu’une commande a des sous-commandes, vous devez généralement spécifier l’une des sous-commandes lors de l’appel d’une application de ligne de commande.Remplacez la méthode du
ReadFile
gestionnaire 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 ressemble maintenant à ceci :
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
Maintenant, si vous essayez 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
définir des couleurs de texte :
scl read --file sampleQuotes.txt --fgcolor red --light-mode
Fournissez une valeur non valide pour --delay
et vous recevrez 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'.
Fournissez 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 aura les commandes et options suivantes :
- commande racine avec une option globale* nommée
--file
- Commande
quotes
read
commande avec des options nommées--delay
,--fgcolor
et--light-mode
add
commande avec des arguments nommésquote
etbyline
delete
commande avec option nommée--search-terms
- Commande
* Une option globale est disponible pour la commande à laquelle elle est affectée et récursivement à toutes ses sous-commandes.
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"
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 par 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 elle est définie
isDefault
surtrue
. Si vous ne définissez pasisDefault
surtrue
, le déléguéparseArgument
n'est pas appelé lorsqu'aucune entrée n'est fournie pour--file
.Après le code qui crée
lightModeOption
, ajoutez des options et des arguments pour les commandesadd
et pour les commandesdelete
: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 AllowMultipleArgumentsPerToken paramètre 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"
Remplacez le code qui crée la commande racine et la
read
commande 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 laread
commande.Ajoute l’option
--file
en tant qu’option globale à la commande racine.Crée une
quotes
commande et l’ajoute à la commande racine.Ajoute la commande
read
à la commandequotes
au lieu de l'ajouter à la commande racine.Crée les commandes
add
etdelete
et les ajoute à la commandequotes
.
Le résultat est la hiérarchie de commandes suivante :
- Commande racine
quotes
read
add
delete
L’application implémente désormais 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 récursivement aux sous-commandes. Étant donné qu’elle
--file
se trouve sur la commande racine, elle sera disponible automatiquement dans toutes les sous-commandes de l’application.Après le
SetHandler
code, ajoutez un nouveauSetHandler
code 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 elle n’est pas une commande feuille. Les sous-commandesread
,add
etdelete
sont des commandes terminales sousquotes
, etSetHandler
est appelée pour chacune d'elles.Ajoutez les gestionnaires pour
add
etdelete
.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.
Soumettez un fichier inexistant à --file
avec la commande read
, et vous obtiendrez 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 permettant 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 afficher 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"
Remarque
Si vous travaillez dans le dossier bin/debug/net6.0, c'est dans ce dossier que vous trouverez le fichier avec des modifications issues des commandes add
et delete
. La copie du fichier dans le dossier du projet reste inchangée.
Étapes suivantes
Dans ce tutoriel, vous avez créé une application de ligne de commande simple qui utilise System.CommandLine
. Pour en savoir plus sur la bibliothèque, consultez System.CommandLine vue d’ensemble.