Not
Åtkomst till denna sida kräver auktorisation. Du kan prova att logga in eller byta katalog.
Åtkomst till denna sida kräver auktorisation. Du kan prova att byta katalog.
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 skapar du en mer komplex app som innehåller flera underkommandon och olika alternativ för varje kommando.
I den här handledningen kommer 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,string[],int,bool,FileInfooch alternativtyper. - Läs alternativvärden i kommandoåtgärdskoden.
- Använd anpassad kod för att parsa och verifiera alternativ.
Förutsättningar
- Den senaste versionen av .NET SDK
- Visual Studio Code-redigerare
- C# DevKit
Eller
- Visual Studio med arbetsbelastningen för .NET-skrivbordsutveckling installerad.
Skapa appen
Skapa ett .NET 9-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 net9.0
Installera System.CommandLine-paketet
Kör följande kommando:
dotnet add package System.CommandLineEller i .NET 10+:
dotnet package add System.CommandLine
Analysera argumenten
Ersätt innehållet i Program.cs med följande kod:
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);
}
}
}
Föregående kod:
- Skapar ett alternativ med namnet
--filetyp FileInfo och lägger till det i rotkommandot:
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);
- Parsar
argsoch kontrollerar om något värde har angetts för--filealternativet. I så fall anropasReadFilemetoden med det parsade värdet och returnerar slutkoden0:
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.Errors.Count == 0 && parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
ReadFile(parsedFile);
return 0;
}
- Om inget värde angavs för
--fileskriver det ut tillgängliga parsningsfel och returnerar slutkod1:
foreach (ParseError parseError in parseResult.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
return 1;
- Metoden
ReadFileläser den angivna filen och visar dess innehåll i konsolen:
static void ReadFile(FileInfo file)
{
foreach (string line in File.ReadLines(file.FullName))
{
Console.WriteLine(line);
}
}
Testa appen
Du kan använda något av följande sätt att testa när du utvecklar en kommandoradsapp:
Kör
dotnet buildkommandot och öppna därefter ett kommandofönster i byggutdata-mappen för att köra den körbara filen.dotnet build cd bin/Debug/net9.0 scl --file scl.runtimeconfig.jsonAnvänd
dotnet runoch skicka alternativvärden till appen i stället för till kommandotrungenom att inkludera dem efter--, som i följande exempel:dotnet run -- --file bin/Debug/net9.0/scl.runtimeconfig.json
(Den här handledningen 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 alternativet --file.
{
"runtimeOptions": {
"tfm": "net9.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
}
}
}
Men om du ber den att visa hjälpen genom att ange --helpskrivs ingenting ut till konsolen. Det beror på att appen ännu inte hanterar scenariot där --file inte tillhandahålls och det inte finns några parsningsfel.
Parsa argumenten och anropa ParseResult
System.CommandLine gör att du kan ange en åtgärd som anropas när en viss symbol (kommando, direktiv eller alternativ) parsas korrekt. Åtgärden är en delegat som tar en ParseResult parameter och returnerar en int slutkod. (Async-åtgärder är också tillgängliga). Slutkoden returneras av ParseResult.Invoke(InvocationConfiguration) metoden och kan användas för att ange om kommandot kördes korrekt eller inte.
Ersätt innehållet i Program.cs med följande kod:
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);
}
}
}
Föregående kod:
Anger att
ReadFileär den metod som anropas när rotkommandot anropas:rootCommand.SetAction(parseResult => { FileInfo parsedFile = parseResult.GetValue(fileOption); ReadFile(parsedFile); return 0; });Parsar
argsoch anropar resultatet:ParseResult parseResult = rootCommand.Parse(args); return parseResult.Invoke();
När du kör appen visas innehållet i filen som anges av alternativet --file.
Om du ber programmet att visa hjälpen genom att specificera scl --help, skrivs följande output ut:
Description:
Sample app for System.CommandLine
Usage:
scl [options]
Options:
-?, -h, --help Show help and usage information
--version Show version information
--file The file to read and display on the console
RootCommand Som standard finns hjälpalternativ, versionsalternativ och förslagsdirektiv. Metoden ParseResult.Invoke(InvocationConfiguration) ansvarar för att anropa åtgärden för den parsade symbolen. Det kan vara den åtgärd som uttryckligen definierats för kommandot eller den hjälpåtgärd som definieras av System.CommandLine för System.CommandLine.Help.HelpOption.
När metoden identifierar eventuella parsningsfel skriver den dessutom Invoke ut dem till standardfelet, skriver ut hjälp med standardutdata och returnerar 1 som slutkod:
scl --invalid bla
Unrecognized command or argument '--invalid'.
Unrecognized command or argument 'bla'.
Lägg till en underkommando och alternativ
I det här avsnittet ska du:
- Skapa fler alternativ.
- Skapa en underkommando.
- Tilldela de nya alternativen till ett nytt underkommando.
Med de nya alternativen kan slutanvändaren konfigurera förgrunds- och bakgrundstextfärgerna och avläsningshastigheten. De här funktionerna kommer att användas för att läsa en samling citat som kommer från Teleprompter-konsolappens självstudie.
Kopiera sampleQuotes.txt-filen från dotnet-exempellagringsplatsen 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 markeringen kopieras textfilen till utdatamappen när du skapar appen. Så när du kör den körbara filen i mappen kan du komma åt filen med hjälp av namn utan att ange en mappsökväg.
I Program.cs, efter koden som skapar alternativet
--file, skapar du alternativ för att styra avläsningshastigheten och textfärgerna:Option<int> delayOption = new("--delay") { Description = "Delay between lines, specified as milliseconds per character in a line", DefaultValueFactory = parseResult => 42 }; Option<ConsoleColor> fgcolorOption = new("--fgcolor") { Description = "Foreground color of text displayed on the console", DefaultValueFactory = parseResult => ConsoleColor.White }; Option<bool> lightModeOption = new("--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 koden som lägger
--filetill alternativet i det. Du tar bort den här eftersom du lägger till den i ett nytt delkommando.Efter raden som skapar rotkommandot skapar du en
readunderkommando. Lägg till alternativen i den här underkommandot (med hjälp av insamlingsinitieringssyntaxen i ställetOptionsför egenskapen) och lägg till underkommandot i rotkommandot.Command readCommand = new("read", "Read and display the file.") { fileOption, delayOption, fgcolorOption, lightModeOption }; rootCommand.Subcommands.Add(readCommand);Ersätt
SetAction-koden med följandeSetActionkod för den nya underkommandot:readCommand.SetAction(parseResult => ReadFile( parseResult.GetValue(fileOption), parseResult.GetValue(delayOption), parseResult.GetValue(fgcolorOption), parseResult.GetValue(lightModeOption)));Du anropar
SetActioninte längre rotkommandot eftersom rotkommandot inte längre behöver en åtgärd. När ett kommando har underkommandon måste du vanligtvis ange en av underkommandona när du anropar en kommandoradsapp.ReadFileErsätt åtgärdsmetoden med följande kod:internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode) { Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black; Console.ForegroundColor = fgColor; foreach (string line in File.ReadLines(file.FullName)) { Console.WriteLine(line); Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length)); } }
Appen ser nu ut så här:
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"
};
Option<int> delayOption = new("--delay")
{
Description = "Delay between lines, specified as milliseconds per character in a line",
DefaultValueFactory = parseResult => 42
};
Option<ConsoleColor> fgcolorOption = new("--fgcolor")
{
Description = "Foreground color of text displayed on the console",
DefaultValueFactory = parseResult => ConsoleColor.White
};
Option<bool> lightModeOption = new("--light-mode")
{
Description = "Background color of text displayed on the console: default is black, light mode is white"
};
RootCommand rootCommand = new("Sample app for System.CommandLine");
Command readCommand = new("read", "Read and display the file.")
{
fileOption,
delayOption,
fgcolorOption,
lightModeOption
};
rootCommand.Subcommands.Add(readCommand);
readCommand.SetAction(parseResult => ReadFile(
parseResult.GetValue(fileOption),
parseResult.GetValue(delayOption),
parseResult.GetValue(fgcolorOption),
parseResult.GetValue(lightModeOption)));
return rootCommand.Parse(args).Invoke();
}
internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
{
Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
Console.ForegroundColor = fgColor;
foreach (string line in File.ReadLines(file.FullName))
{
Console.WriteLine(line);
Thread.Sleep(TimeSpan.FromMilliseconds(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:
-?, -h, --help Show help and usage information
--version Show version information
Commands:
read Read and display the file.
Hjälptexten för underkommandon read visar att fyra alternativ är tillgängliga. Den visar giltiga värden för enum.
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 anger endast alternativet --file 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 till 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
Om du anger ett ogiltigt värde för --delayfår du ett felmeddelande:
scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.
Om du anger ett ogiltigt värde för --filefår du ett undantag:
scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException: Could not find file 'C:\bin\Debug\net9.0\nofile''.
File name: 'C:\bin\Debug\net9.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:
| Command | Options | Arguments |
|---|---|---|
| Rotkommando |
--file(rekursiv) |
|
quotes |
||
read |
--delay, --fgcolor--light-mode |
|
add |
quote och byline |
|
delete |
--search-terms |
(Ett rekursivt 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.csersätter du koden som skapar alternativet
--filemed följande kod:Option<FileInfo> fileOption = new("--file") { Description = "An option whose argument is parsed as a FileInfo", Required = true, DefaultValueFactory = result => { if (result.Tokens.Count == 0) { return new FileInfo("sampleQuotes.txt"); } string filePath = result.Tokens.Single().Value; if (!File.Exists(filePath)) { result.AddError("File does not exist"); return null; } else { return new FileInfo(filePath); } } };Den här koden använder ArgumentResult för att tillhandahålla anpassad parsning, validering och felhantering.
Utan den här koden rapporteras saknade filer med ett undantag 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 ställer in Option<T>.DefaultValueFactory till en anpassad parsningsmetod.
Efter koden som skapar
lightModeOptionlägger du till alternativ och argument för kommandonaaddochdelete:Option<string[]> searchTermsOption = new("--search-terms") { Description = "Strings to search for when deleting entries", Required = true, AllowMultipleArgumentsPerToken = true }; Argument<string> quoteArgument = new("quote") { Description = "Text of quote." }; Argument<string> bylineArgument = new("byline") { Description = "Byline of quote." };Med inställningen AllowMultipleArgumentsPerToken kan du utelämna namnet på
--search-termsnär du specificerar element i listan efter det första elementet. Det gör följande exempel på kommandoradsindata likvärdiga: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 kommandot
readmed följande kod:RootCommand rootCommand = new("Sample app for System.CommandLine"); fileOption.Recursive = true; rootCommand.Options.Add(fileOption); Command quotesCommand = new("quotes", "Work with a file that contains quotes."); rootCommand.Subcommands.Add(quotesCommand); Command readCommand = new("read", "Read and display the file.") { delayOption, fgcolorOption, lightModeOption }; quotesCommand.Subcommands.Add(readCommand); Command deleteCommand = new("delete", "Delete lines from the file."); deleteCommand.Options.Add(searchTermsOption); quotesCommand.Subcommands.Add(deleteCommand); Command addCommand = new("add", "Add an entry to the file."); addCommand.Arguments.Add(quoteArgument); addCommand.Arguments.Add(bylineArgument); addCommand.Aliases.Add("insert"); quotesCommand.Subcommands.Add(addCommand);Den här koden gör följande ändringar:
Tar bort alternativet
--filefrån kommandotread.Lägger till alternativet
--filesom ett rekursivt alternativ i rotkommandot.Skapar ett
quoteskommando och lägger till det i rotkommandot.Lägger till kommandot
readi kommandotquotesi stället för till rotkommandot.Skapar kommandon för
addochdeleteoch lägger till dem i kommandotquotes.
Resultatet är följande kommandohierarki:
Rotkommando └──
quotes└──read└──add└──deleteAppen 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.Rekursiva alternativ tillämpas på kommandot och rekursivt på underkommandon. Eftersom
--filefinns i rotkommandot blir det automatiskt tillgängligt i alla underkommandon i appen.Efter
SetAction-koden, lägg till nyaSetAction-koden för de nya underkommandona:deleteCommand.SetAction(parseResult => DeleteFromFile( parseResult.GetValue(fileOption), parseResult.GetValue(searchTermsOption))); addCommand.SetAction(parseResult => AddToFile( parseResult.GetValue(fileOption), parseResult.GetValue(quoteArgument), parseResult.GetValue(bylineArgument)) );Underkommandot
quoteshar ingen åtgärd eftersom det inte är ett lövkommando. Underkommandonread,addochdeleteär lövkommandon underquotesochSetActionanropas för var och en av dem.Lägg till åtgärderna för
addochdelete.internal static void DeleteFromFile(FileInfo file, string[] searchTerms) { Console.WriteLine("Deleting from file"); var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s))); File.WriteAllLines(file.FullName, lines); } 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}"); }
Den färdiga appen ser ut så här:
using System.CommandLine;
namespace scl;
class Program
{
static int Main(string[] args)
{
Option<FileInfo> fileOption = new("--file")
{
Description = "An option whose argument is parsed as a FileInfo",
Required = true,
DefaultValueFactory = result =>
{
if (result.Tokens.Count == 0)
{
return new FileInfo("sampleQuotes.txt");
}
string filePath = result.Tokens.Single().Value;
if (!File.Exists(filePath))
{
result.AddError("File does not exist");
return null;
}
else
{
return new FileInfo(filePath);
}
}
};
Option<int> delayOption = new("--delay")
{
Description = "Delay between lines, specified as milliseconds per character in a line",
DefaultValueFactory = parseResult => 42
};
Option<ConsoleColor> fgcolorOption = new("--fgcolor")
{
Description = "Foreground color of text displayed on the console",
DefaultValueFactory = parseResult => ConsoleColor.White
};
Option<bool> lightModeOption = new("--light-mode")
{
Description = "Background color of text displayed on the console: default is black, light mode is white"
};
Option<string[]> searchTermsOption = new("--search-terms")
{
Description = "Strings to search for when deleting entries",
Required = true,
AllowMultipleArgumentsPerToken = true
};
Argument<string> quoteArgument = new("quote")
{
Description = "Text of quote."
};
Argument<string> bylineArgument = new("byline")
{
Description = "Byline of quote."
};
RootCommand rootCommand = new("Sample app for System.CommandLine");
fileOption.Recursive = true;
rootCommand.Options.Add(fileOption);
Command quotesCommand = new("quotes", "Work with a file that contains quotes.");
rootCommand.Subcommands.Add(quotesCommand);
Command readCommand = new("read", "Read and display the file.")
{
delayOption,
fgcolorOption,
lightModeOption
};
quotesCommand.Subcommands.Add(readCommand);
Command deleteCommand = new("delete", "Delete lines from the file.");
deleteCommand.Options.Add(searchTermsOption);
quotesCommand.Subcommands.Add(deleteCommand);
Command addCommand = new("add", "Add an entry to the file.");
addCommand.Arguments.Add(quoteArgument);
addCommand.Arguments.Add(bylineArgument);
addCommand.Aliases.Add("insert");
quotesCommand.Subcommands.Add(addCommand);
readCommand.SetAction(parseResult => ReadFile(
parseResult.GetValue(fileOption),
parseResult.GetValue(delayOption),
parseResult.GetValue(fgcolorOption),
parseResult.GetValue(lightModeOption)));
deleteCommand.SetAction(parseResult => DeleteFromFile(
parseResult.GetValue(fileOption),
parseResult.GetValue(searchTermsOption)));
addCommand.SetAction(parseResult => AddToFile(
parseResult.GetValue(fileOption),
parseResult.GetValue(quoteArgument),
parseResult.GetValue(bylineArgument))
);
return rootCommand.Parse(args).Invoke();
}
internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
{
Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
Console.ForegroundColor = fgColor;
foreach (string line in File.ReadLines(file.FullName))
{
Console.WriteLine(line);
Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
}
}
internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
{
Console.WriteLine("Deleting from file");
var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s)));
File.WriteAllLines(file.FullName, lines);
}
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}");
}
}
Skapa projektet och prova sedan följande kommandon.
Skicka en obefintlig fil till --file med kommandot read och du får ett felmeddelande i stället för ett undantag och stackspårning:
scl quotes read --file nofile
File does not exist
Om du försöker köra underkommandot quotesfår du ett meddelande som instruerar dig att använda read, addeller 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 addoch titta sedan på 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 på början av textfilen för att se var texten har tagits bort:
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
Anmärkning
Filen i utdatamappen återspeglar ändringarna från kommandona add och delete . 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 översikt. För att aktivera flikkompletteringsfunktioner, se Flikkomplettering för System.CommandLine.