Självstudie: Kom igång med System.CommandLine
Viktigt
System.CommandLine
är för närvarande i förhandsversion, och den här dokumentationen är för version 2.0 beta 4.
Viss information gäller förhandsversionsprodukt som kan ändras avsevärt innan den släpps. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, avseende informationen som visas här.
Den här självstudien visar hur du skapar en .NET-kommandoradsapp som använder System.CommandLine
biblioteket. Du börjar med att skapa ett enkelt rotkommando som har ett alternativ. Sedan lägger du till i den basen och skapar en mer komplex app som innehåller flera underkommandon och olika alternativ för varje kommando.
I den här guiden får du lära dig att:
- Skapa kommandon, alternativ och argument.
- Ange standardvärden för alternativ.
- Tilldela alternativ och argument till kommandon.
- Tilldela ett alternativ rekursivt till alla underkommandon under ett kommando.
- Arbeta med flera nivåer av kapslade underkommandon.
- Skapa alias för kommandon och alternativ.
- Arbeta med
string
alternativtyperna ,string[]
,int
,bool
FileInfo
och uppräkning. - Binda alternativvärden till kommandohanterarkod.
- Använd anpassad kod för att parsa och verifiera alternativ.
Förutsättningar
- En kodredigerare, till exempel Visual Studio Code med C#-tillägget.
- .NET 6 SDK.
Eller
- Visual Studio 2022 med arbetsbelastningen för .NET-skrivbordsutveckling installerad.
Skapa appen
Skapa ett .NET 6-konsolappprojekt med namnet "scl".
Skapa en mapp med namnet scl för projektet och öppna sedan en kommandotolk i den nya mappen.
Kör följande kommando:
dotnet new console --framework net6.0
Installera System.CommandLine-paketet
Kör följande kommando:
dotnet add package System.CommandLine --prerelease
Alternativet
--prerelease
är nödvändigt eftersom biblioteket fortfarande är i betaversion.
Ersätt innehållet i Program.cs med följande kod:
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)); } }
Koden ovan:
Skapar ett alternativ med namnet
--file
av typen FileInfo och tilldelar det till rotkommandot: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);
Anger att
ReadFile
är den metod som anropas när rotkommandot anropas:rootCommand.SetHandler((file) => { ReadFile(file!); }, fileOption);
Visar innehållet i den angivna filen när rotkommandot anropas:
static void ReadFile(FileInfo file) { File.ReadLines(file.FullName).ToList() .ForEach(line => Console.WriteLine(line)); }
Testa appen
Du kan använda något av följande sätt att testa när du utvecklar en kommandoradsapp:
dotnet build
Kör kommandot och öppna sedan en kommandotolk i mappen scl/bin/Debug/net6.0 för att köra den körbara filen:dotnet build cd bin/Debug/net6.0 scl --file scl.runtimeconfig.json
Använd
dotnet run
och skicka alternativvärden till appen i stället för till kommandot genom attrun
inkludera dem efter--
, som i följande exempel:dotnet run -- --file scl.runtimeconfig.json
I .NET 7.0.100 SDK Preview kan du använda
commandLineArgs
filen launchSettings.json genom att köra kommandotdotnet run --launch-profile <profilename>
.Publicera projektet i en mapp, öppna en kommandotolk till mappen och kör den körbara filen:
dotnet publish -o publish cd ./publish scl --file scl.runtimeconfig.json
I Visual Studio 2022 väljer du Felsöka>felsökningsegenskaper på menyn och anger alternativen och argumenten i rutan Kommandoradsargument . Exempel:
Kör sedan appen, till exempel genom att trycka på Ctrl+F5.
Den här självstudien förutsätter att du använder det första av dessa alternativ.
När du kör appen visas innehållet i filen som anges av --file
alternativet .
{
"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
}
}
}
Hjälputdata
System.CommandLine
ger automatiskt hjälputdata:
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
Versionsutdata
System.CommandLine
tillhandahåller automatiskt versionsutdata:
scl --version
1.0.0
Lägg till en underkommando och alternativ
I det här avsnittet får du:
- Skapa fler alternativ.
- Skapa en underkommando.
- Tilldela de nya alternativen till den nya underkommandot.
Med de nya alternativen kan du konfigurera förgrunds- och bakgrundstextfärgerna och avläsningshastigheten. De här funktionerna används för att läsa en samling citattecken som kommer från självstudiekursen för Teleprompter-konsolappen.
Kopiera sampleQuotes.txt-filen från GitHub-lagringsplatsen för det här exemplet till projektkatalogen. Information om hur du laddar ned filer finns i anvisningarna i Exempel och självstudier.
Öppna projektfilen och lägg till ett
<ItemGroup>
element precis före den avslutande</Project>
taggen:<ItemGroup> <Content Include="sampleQuotes.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
Om du lägger till den här pålägget kopieras textfilen till mappen bin/debug/net6.0 när du skapar appen. Så när du kör den körbara filen i mappen kan du komma åt filen efter namn utan att ange en mappsökväg.
Efter koden som skapar
--file
alternativet i Program.cs skapar du alternativ för att styra avläsningshastigheten och textfärgerna: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.");
Efter raden som skapar rotkommandot tar du bort raden som lägger till
--file
alternativet i det. Du tar bort den här eftersom du lägger till den i en ny underkommando.var rootCommand = new RootCommand("Sample app for System.CommandLine"); //rootCommand.AddOption(fileOption);
Efter raden som skapar rotkommandot skapar du en
read
underkommando. Lägg till alternativen i den här underkommandot och lägg till underkommandot i rotkommandot.var readCommand = new Command("read", "Read and display the file.") { fileOption, delayOption, fgcolorOption, lightModeOption }; rootCommand.AddCommand(readCommand);
SetHandler
Ersätt koden med följandeSetHandler
kod för den nya underkommandot:readCommand.SetHandler(async (file, delay, fgcolor, lightMode) => { await ReadFile(file!, delay, fgcolor, lightMode); }, fileOption, delayOption, fgcolorOption, lightModeOption);
Du anropar
SetHandler
inte längre rotkommandot eftersom rotkommandot inte längre behöver en hanterare. När ett kommando har underkommandon måste du vanligtvis ange en av underkommandona när du anropar en kommandoradsapp.ReadFile
Ersätt hanteringsmetoden med följande kod: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); }; }
Appen ser nu ut så här:
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);
};
}
}
Testa den nya underkommandot
Om du nu försöker köra appen utan att ange underkommandot får du ett felmeddelande följt av ett hjälpmeddelande som anger den underkommando som är tillgänglig.
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.
Hjälptexten för underkommandot read
visar att fyra alternativ är tillgängliga. Den visar giltiga värden för uppräkningen.
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
Kör underkommandot read
och ange endast --file
alternativet och du får standardvärdena för de andra tre alternativen.
scl read --file sampleQuotes.txt
Standardfördröjningen på 42 millisekunder per tecken orsakar en långsam avläsningshastighet. Du kan påskynda det genom att ange --delay
ett lägre tal.
scl read --file sampleQuotes.txt --delay 0
Du kan använda --fgcolor
och --light-mode
för att ange textfärger:
scl read --file sampleQuotes.txt --fgcolor red --light-mode
Ange ett ogiltigt värde för --delay
och du får ett felmeddelande:
scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.
Ange ett ogiltigt värde för --file
och du får ett undantag:
scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException:
Could not find file 'C:\bin\Debug\net6.0\nofile'.
Lägga till underkommandon och anpassad validering
Det här avsnittet skapar den slutliga versionen av appen. När appen är klar har den följande kommandon och alternativ:
- rotkommando med ett alternativ för global* med namnet
--file
- Kommandot
quotes
read
kommandot med alternativen--delay
,--fgcolor
och--light-mode
add
kommando med argument med namnetquote
ochbyline
delete
kommando med alternativet med namnet--search-terms
- Kommandot
* Ett globalt alternativ är tillgängligt för kommandot som det tilldelas till och rekursivt till alla dess underkommandon.
Här är exempel på kommandoradsindata som anropar vart och ett av de tillgängliga kommandona med dess alternativ och argument:
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"
I Program.cs ersätter du koden som skapar
--file
alternativet med följande kod: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); } });
Den här koden används ParseArgument<T> för att tillhandahålla anpassad parsning, validering och felhantering.
Utan den här koden rapporteras saknade filer med ett undantags- och stackspårning. Med den här koden visas bara det angivna felmeddelandet.
Den här koden anger också ett standardvärde, vilket är anledningen till att den anger
isDefault
tilltrue
. Om du inte angerisDefault
tilltrue
anropas inte ombudetparseArgument
när inga indata anges för--file
.Efter koden som skapar
lightModeOption
lägger du till alternativ och argument för kommandonaadd
ochdelete
: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.");
Med AllowMultipleArgumentsPerToken inställningen kan du utelämna
--search-terms
alternativnamnet när du anger element i listan efter det första. Det gör följande exempel på motsvarande kommandoradsindata:scl quotes delete --search-terms David "You can do" scl quotes delete --search-terms David --search-terms "You can do"
Ersätt koden som skapar rotkommandot och
read
kommandot med följande kod: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);
Den här koden gör följande ändringar:
Tar
--file
bort alternativet frånread
kommandot .--file
Lägger till alternativet som ett globalt alternativ i rotkommandot.Skapar ett
quotes
kommando och lägger till det i rotkommandot.read
Lägger till kommandot iquotes
kommandot i stället för till rotkommandot.Skapar
add
ochdelete
kommandon och lägger till dem iquotes
kommandot .
Resultatet är följande kommandohierarki:
- Rotkommando
quotes
read
add
delete
Appen implementerar nu det rekommenderade mönstret där det överordnade kommandot (
quotes
) anger ett område eller en grupp och dess underordnade kommandon (read
,add
,delete
) är åtgärder.Globala alternativ tillämpas på kommandot och rekursivt på underkommandon. Eftersom
--file
finns i rotkommandot blir det automatiskt tillgängligt i alla underkommandon i appen.SetHandler
Efter koden lägger du till nySetHandler
kod för de nya underkommandona:deleteCommand.SetHandler((file, searchTerms) => { DeleteFromFile(file!, searchTerms); }, fileOption, searchTermsOption); addCommand.SetHandler((file, quote, byline) => { AddToFile(file!, quote, byline); }, fileOption, quoteArgument, bylineArgument);
Underkommandot
quotes
har ingen hanterare eftersom det inte är ett lövkommando. Underkommandonread
,add
ochdelete
är lövkommandon underquotes
och anropas för var ochSetHandler
en av dem.Lägg till hanterare för
add
ochdelete
.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(); }
Den färdiga appen ser ut så här:
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();
}
}
Skapa projektet och prova sedan följande kommandon.
Skicka en obefintlig fil till --file
med read
kommandot så får du ett felmeddelande i stället för ett undantag och stackspårning:
scl quotes read --file nofile
File does not exist
Försök att köra underkommandot quotes
så får du ett meddelande som uppmanar dig att använda read
, add
eller 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.
Kör underkommandot add
och titta sedan i slutet av textfilen för att se den tillagda texten:
scl quotes add "Hello world!" "Nancy Davolio"
Kör underkommandot delete
med söksträngar från början av filen och titta sedan i början av textfilen för att se var texten togs bort:
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
Anteckning
Om du kör i mappen bin/debug/net6.0 hittar du filen med ändringar från add
kommandona och delete
i den mappen. Kopian av filen i projektmappen förblir oförändrad.
Nästa steg
I den här självstudien skapade du en enkel kommandoradsapp som använder System.CommandLine
. Mer information om biblioteket finns i System.CommandLine översikten.