Kurz: Začínáme s System.CommandLine
Důležité
System.CommandLine
je aktuálně ve verzi PREVIEW a tato dokumentace je určená pro verzi 2.0 beta 4.
Některé informace se týkají předběžného vydání produktu, který může být před vydáním podstatně změněn. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
V tomto kurzu se dozvíte, jak vytvořit aplikaci příkazového řádku .NET, která používá knihovnuSystem.CommandLine
. Začnete vytvořením jednoduchého kořenového příkazu, který má jednu možnost. Pak do této základny přidáte složitější aplikaci, která bude obsahovat několik dílčích příkazů a různé možnosti pro každý příkaz.
V tomto kurzu se naučíte:
- Umožňuje vytvářet příkazy, možnosti a argumenty.
- Zadejte výchozí hodnoty pro možnosti.
- Přiřaďte k příkazům možnosti a argumenty.
- Přiřaďte možnost rekurzivně všem dílčím příkazům v rámci příkazu.
- Práce s několika úrovněmi vnořených dílčích příkazů
- Vytváření aliasů pro příkazy a možnosti
- Práce s
string
typy možností ,string[]
,int
,bool
FileInfo
a výčtu - Vytvořte vazbu hodnot možností na kód obslužné rutiny příkazu.
- Pro parsování a ověřování možností použijte vlastní kód.
Požadavky
- Editor kódu, například Visual Studio Code s rozšířením jazyka C#.
- Sada .NET 6 SDK.
Nebo
- Visual Studio 2022 s nainstalovanou úlohou vývoj desktopových aplikací .NET
Vytvoření aplikace
Vytvořte projekt konzolové aplikace .NET 6 s názvem "scl".
Vytvořte složku s názvem scl pro projekt a pak otevřete příkazový řádek v nové složce.
Spusťte následující příkaz:
dotnet new console --framework net6.0
Nainstalujte balíček System.CommandLine.
Spusťte následující příkaz:
dotnet add package System.CommandLine --prerelease
Možnost
--prerelease
je nezbytná, protože knihovna je stále v beta verzi.
Obsah souboru Program.cs nahraďte tímto kódem:
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)); } }
Předchozí kód:
Vytvoří možnost typu
--file
FileInfo a přiřadí ji ke kořenovému příkazu: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);
Určuje, že
ReadFile
se jedná o metodu, která bude volána při vyvolání kořenového příkazu:rootCommand.SetHandler((file) => { ReadFile(file!); }, fileOption);
Zobrazí obsah zadaného souboru při vyvolání kořenového příkazu:
static void ReadFile(FileInfo file) { File.ReadLines(file.FullName).ToList() .ForEach(line => Console.WriteLine(line)); }
Otestování aplikace
Při vývoji aplikace příkazového řádku můžete testovat libovolným z následujících způsobů:
dotnet build
Spusťte příkaz a otevřete příkazový řádek ve složce scl/bin/Debug/net6.0 a spusťte spustitelný soubor:dotnet build cd bin/Debug/net6.0 scl --file scl.runtimeconfig.json
Hodnoty možností použijte
dotnet run
a předejte do aplikace místorun
do příkazu tak, že je zahrnete za--
, jako v následujícím příkladu:dotnet run -- --file scl.runtimeconfig.json
V .NET 7.0.100 SDK Preview můžete použít
commandLineArgs
soubor launchSettings.json spuštěním příkazudotnet run --launch-profile <profilename>
.Publikujte projekt do složky, otevřete v této složce příkazový řádek a spusťte spustitelný soubor:
dotnet publish -o publish cd ./publish scl --file scl.runtimeconfig.json
V sadě Visual Studio 2022 vyberte z nabídky Ladit>vlastnosti ladění a zadejte možnosti a argumenty do pole Argumenty příkazového řádku . Například:
Pak aplikaci spusťte, například stisknutím kláves Ctrl+F5.
V tomto kurzu se předpokládá, že používáte první z těchto možností.
Když aplikaci spustíte, zobrazí se obsah souboru určený --file
možností.
{
"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
}
}
}
Výstup nápovědy
System.CommandLine
automaticky poskytuje výstup nápovědy:
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
Výstup verze
System.CommandLine
automaticky poskytuje výstup verze:
scl --version
1.0.0
Přidání podpříkazu a možností
V této části:
- Vytvořte další možnosti.
- Vytvořte podpříkaz.
- Nové možnosti přiřaďte novému podpříkazu.
Nové možnosti vám umožní nakonfigurovat barvy textu na popředí a pozadí a rychlost čtení. Tyto funkce se použijí ke čtení kolekce uvozovek, které pocházejí z kurzu konzolové aplikace Teleprompter.
Zkopírujte souborsampleQuotes.txt z úložiště GitHub pro tuto ukázku do adresáře projektu. Informace o tom, jak stahovat soubory, najdete v pokynech v části Ukázky a kurzy.
Otevřete soubor projektu a přidejte
<ItemGroup>
prvek těsně před uzavírací</Project>
značku:<ItemGroup> <Content Include="sampleQuotes.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
Přidání tohoto kódu způsobí zkopírování textového souboru do složky bin/debug/net6.0 při sestavování aplikace. Když tedy spustíte spustitelný soubor v této složce, můžete k souboru přistupovat podle názvu bez zadání cesty ke složce.
V souboru Program.cs za kódem, který možnost vytvoří
--file
, vytvořte možnosti pro řízení rychlosti čtení a barev textu: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.");
Po řádku, který vytváří kořenový příkaz, odstraňte řádek, který k němu přidá
--file
možnost. Tady ho odebíráte, protože ho přidáte do nového podpříkazu.var rootCommand = new RootCommand("Sample app for System.CommandLine"); //rootCommand.AddOption(fileOption);
Za řádkem, který vytvoří kořenový příkaz, vytvořte
read
dílčí příkaz. Přidejte možnosti do tohoto podpříkazu a přidejte dílčí příkaz do kořenového příkazu.var readCommand = new Command("read", "Read and display the file.") { fileOption, delayOption, fgcolorOption, lightModeOption }; rootCommand.AddCommand(readCommand);
SetHandler
Nahraďte kód následujícímSetHandler
kódem pro nový podpříkaz:readCommand.SetHandler(async (file, delay, fgcolor, lightMode) => { await ReadFile(file!, delay, fgcolor, lightMode); }, fileOption, delayOption, fgcolorOption, lightModeOption);
Už nevoláte
SetHandler
kořenový příkaz, protože kořenový příkaz už nepotřebuje obslužnou rutinu. Pokud má příkaz dílčí příkazy, obvykle musíte při vyvolání aplikace příkazového řádku zadat jeden z dílčích příkazů.Nahraďte metodu
ReadFile
obslužné rutiny následujícím kódem: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); }; }
Aplikace teď vypadá takto:
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);
};
}
}
Testování nového podpříkazu
Když se teď pokusíte aplikaci spustit bez zadání dílčího příkazu, zobrazí se chybová zpráva následovaná zprávou nápovědy, která určuje dílčí příkaz, který je k dispozici.
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.
Text nápovědy pro podpříkaz read
ukazuje, že jsou k dispozici čtyři možnosti. Zobrazí platné hodnoty výčtu.
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
Spusťte podpříkaz read
s určením --file
pouze možnosti a získáte výchozí hodnoty pro další tři možnosti.
scl read --file sampleQuotes.txt
Výchozí zpoždění 42 milisekund na znak způsobuje zpomalení čtení. Můžete ho urychlit nastavením --delay
na nižší číslo.
scl read --file sampleQuotes.txt --delay 0
Barvy textu můžete nastavit pomocí a --fgcolor
--light-mode
:
scl read --file sampleQuotes.txt --fgcolor red --light-mode
Zadejte neplatnou hodnotu a --delay
zobrazí se chybová zpráva:
scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.
Zadejte neplatnou hodnotu a --file
zobrazí se výjimka:
scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException:
Could not find file 'C:\bin\Debug\net6.0\nofile'.
Přidání dílčích příkazů a vlastního ověření
Tato část vytvoří konečnou verzi aplikace. Po dokončení bude mít aplikace následující příkazy a možnosti:
- kořenový příkaz s globální* možností s názvem
--file
- Příkaz
quotes
read
příkaz s možnostmi s názvy--delay
,--fgcolor
a--light-mode
add
příkaz s argumenty s názvemquote
abyline
delete
command with option named--search-terms
- Příkaz
* Globální možnost je k dispozici pro příkaz, ke kterému je přiřazený, a rekurzivně pro všechny jeho dílčí příkazy.
Tady je ukázkový vstup příkazového řádku, který vyvolá každý z dostupných příkazů s možnostmi a argumenty:
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"
V souboru Program.cs nahraďte kód, který možnost vytvoří
--file
, následujícím kódem: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); } });
Tento kód používá ParseArgument<T> k poskytování vlastní analýzy, ověřování a zpracování chyb.
Bez tohoto kódu se chybějící soubory hlásí s výjimkou a trasování zásobníku. S tímto kódem se zobrazí pouze zadaná chybová zpráva.
Tento kód také určuje výchozí hodnotu, a proto se nastaví
isDefault
natrue
. Pokud na hodnotu nenastavíteisDefault
true
,parseArgument
delegát se nezavolá, pokud není zadaný žádný vstup pro--file
.Za kód, který vytvoří
lightModeOption
, přidejte možnosti a argumenty proadd
příkazy adelete
: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.");
Nastavení AllowMultipleArgumentsPerToken umožňuje vynechat
--search-terms
název možnosti při zadávání prvků v seznamu za prvním z nich. Následující příklady zadávání příkazového řádku jsou ekvivalentní:scl quotes delete --search-terms David "You can do" scl quotes delete --search-terms David --search-terms "You can do"
Nahraďte kód, který vytvoří kořenový příkaz, a
read
příkaz následujícím kódem: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);
Tento kód provede následující změny:
Odebere
--file
možnost zread
příkazu.--file
Přidá možnost jako globální možnost do kořenového příkazu.Vytvoří
quotes
příkaz a přidá ho do kořenového příkazu.read
Přidá příkaz do příkazuquotes
místo do kořenového příkazu.Vytvoří
add
příkazy adelete
a přidá je do příkazuquotes
.
Výsledkem je následující hierarchie příkazů:
- Kořenový příkaz
quotes
read
add
delete
Aplikace teď implementuje doporučený vzor, kdy nadřazený příkaz (
quotes
) určuje oblast nebo skupinu a jeho podřízené příkazy (read
,add
,delete
) jsou akce.Globální možnosti se použijí na příkaz a rekurzivně na dílčí příkazy. Vzhledem k tomu
--file
, že je v kořenovém příkazu, bude automaticky k dispozici ve všech dílčích příkazech aplikace.SetHandler
Za kód přidejte novýSetHandler
kód pro nové podpříkazy:deleteCommand.SetHandler((file, searchTerms) => { DeleteFromFile(file!, searchTerms); }, fileOption, searchTermsOption); addCommand.SetHandler((file, quote, byline) => { AddToFile(file!, quote, byline); }, fileOption, quoteArgument, bylineArgument);
Podpříkaz
quotes
nemá obslužnou rutinu, protože se nejedná o příkaz list. Podpříkazyread
,add
adelete
jsou příkazy typu list v částiquotes
aSetHandler
jsou voláné pro každou z nich.Přidejte obslužné rutiny pro
add
adelete
.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(); }
Hotová aplikace vypadá takto:
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();
}
}
Sestavte projekt a pak zkuste následující příkazy.
Pomocí příkazu odešlete neexistující soubor do --file
read
a místo výjimky a trasování zásobníku se zobrazí chybová zpráva:
scl quotes read --file nofile
File does not exist
Zkuste spustit podpříkaz quotes
a zobrazí se zpráva, která vás nasměruje k použití read
, add
nebo 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.
Spusťte podpříkaz add
a podívejte se na konec textového souboru, abyste viděli přidaný text:
scl quotes add "Hello world!" "Nancy Davolio"
Spusťte podpříkaz delete
s vyhledávacími řetězci od začátku souboru a pak se podívejte na začátek textového souboru, abyste zjistili, kde byl text odebrán:
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
Poznámka
Pokud používáte soubor ve složce bin/debug/net6.0 , najdete v této složce soubor se změnami z add
příkazů a delete
. Kopie souboru ve složce projektu zůstane beze změny.
Další kroky
V tomto kurzu jste vytvořili jednoduchou aplikaci příkazového řádku, která používá System.CommandLine
. Další informace o knihovně najdete v přehleduSystem.CommandLine.