Руководство. Начало работы с System.CommandLine
Важно!
System.CommandLine
В настоящее время доступна предварительная версия, и эта документация предназначена для бета-версии 4 версии 2.0.
Некоторые сведения относятся к предварительной версии продукта, который может быть существенно изменен перед выпуском. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В этом руководстве показано, как создать приложение командной строки .NET, использующее библиотекуSystem.CommandLine
. Начните с создания простой корневой команды, которая имеет один вариант. Затем вы добавите в эту базу, создав более сложное приложение, содержащее несколько подкоманд и различные параметры для каждой команды.
В этом руководстве описано следующее:
- Создание команд, параметров и аргументов.
- Укажите значения по умолчанию для параметров.
- Назначение параметров и аргументов командам.
- Рекурсивное назначение параметра всем подкомандам в команде.
- Работа с несколькими уровнями вложенных подкомандов.
- Создание псевдонимов для команд и параметров.
- Работа с типами
string
параметров перечисления ,string[]
int
, ,bool
FileInfo
и . - Привяжите значения параметров к коду обработчика команд.
- Используйте пользовательский код для анализа и проверки параметров.
Предварительные требования
- Редактор кода, например Visual Studio Code с расширением C#.
- Пакет SDK для .NET 6.
либо
- Visual Studio 2022 с установленной рабочей нагрузкой разработка классических приложений .NET .
Создание приложения
Создайте проект консольного приложения .NET 6 с именем "scl".
Создайте папку с именем scl для проекта, а затем откройте командную строку в новой папке.
Выполните следующую команду:
dotnet new console --framework net6.0
Установите пакет System.CommandLine.
Выполните следующую команду:
dotnet add package System.CommandLine --prerelease
Параметр
--prerelease
необходим, так как библиотека все еще находится в бета-версии.
Замените содержимое Program.cs кодом из этого примера.
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)); } }
Предыдущий код:
Создает параметр с именем
--file
типа FileInfo и назначает его корневой команде: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);
Указывает, что
ReadFile
является методом, который будет вызываться при вызове корневой команды:rootCommand.SetHandler((file) => { ReadFile(file!); }, fileOption);
Отображает содержимое указанного файла при вызове корневой команды:
static void ReadFile(FileInfo file) { File.ReadLines(file.FullName).ToList() .ForEach(line => Console.WriteLine(line)); }
Тестирование приложения
При разработке приложения командной строки можно использовать любой из следующих способов тестирования:
dotnet build
Выполните команду , а затем откройте командную строку в папке scl/bin/Debug/net6.0, чтобы запустить исполняемый файл:dotnet build cd bin/Debug/net6.0 scl --file scl.runtimeconfig.json
Используйте
dotnet run
и передайте значения параметров в приложение, а неrun
в команду , включив их после--
, как показано в следующем примере:dotnet run -- --file scl.runtimeconfig.json
В предварительной версии пакета SDK для .NET 7.0.100 можно использовать
commandLineArgs
файл файла launchSettings.json , выполнив командуdotnet run --launch-profile <profilename>
.Опубликуйте проект в папке, откройте командную строку в этой папке и запустите исполняемый файл:
dotnet publish -o publish cd ./publish scl --file scl.runtimeconfig.json
В Visual Studio 2022 выберитев меню Свойства отладки> и введите параметры и аргументы в поле Аргументы командной строки. Пример:
Затем запустите приложение, например нажав клавиши CTRL+F5.
В этом руководстве предполагается, что вы используете первый из этих вариантов.
При запуске приложения отображается содержимое файла, указанного параметром --file
.
{
"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
}
}
}
Выходные данные справки
System.CommandLine
автоматически предоставляет выходные данные справки:
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
Выходные данные версии
System.CommandLine
автоматически предоставляет выходные данные версии:
scl --version
1.0.0
Добавление подкоманды и параметров
В этом разделе выполняются следующие действия:
- Создайте дополнительные параметры.
- Создайте подкоманду.
- Назначьте новые параметры новой подкоманде.
Новые параметры позволяют настроить цвета текста переднего плана и фона, а также скорость считывания. Эти функции будут использоваться для чтения коллекции кавычек из руководства по консольным приложениям Teleprompter.
Скопируйте в каталог проекта файл sampleQuotes.txt из репозитория GitHub для этого примера. Сведения о скачивании файлов см. в разделе Примеры и учебники.
Откройте файл проекта и добавьте
<ItemGroup>
элемент непосредственно перед закрывающим</Project>
тегом:<ItemGroup> <Content Include="sampleQuotes.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
При добавлении этой разметки текстовый файл копируется в папку bin/debug/net6.0 при сборке приложения. Таким образом, при запуске исполняемого файла в этой папке вы можете получить доступ к файлу по имени, не указывая путь к папке.
В файле Program.cs после кода, который создает
--file
параметр, создайте параметры для управления скоростью считывания и цветами текста: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.");
После строки, создающей корневую команду, удалите строку, которая добавляет в нее
--file
параметр . Вы удаляете его здесь, так как вы добавите его в новую подкоманду.var rootCommand = new RootCommand("Sample app for System.CommandLine"); //rootCommand.AddOption(fileOption);
После строки, создающей корневую
read
команду, создайте подкоманду. Добавьте параметры в эту подкоманду, а подкоманду — в команду root.var readCommand = new Command("read", "Read and display the file.") { fileOption, delayOption, fgcolorOption, lightModeOption }; rootCommand.AddCommand(readCommand);
Замените
SetHandler
код следующимSetHandler
кодом для новой подкоманды:readCommand.SetHandler(async (file, delay, fgcolor, lightMode) => { await ReadFile(file!, delay, fgcolor, lightMode); }, fileOption, delayOption, fgcolorOption, lightModeOption);
Вы больше не вызываете
SetHandler
команду root, так как ей больше не нужен обработчик. Если команда содержит подкоманды, обычно при вызове приложения командной строки необходимо указать одну из подкоманд.Замените метод обработчика
ReadFile
следующим кодом: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); }; }
Теперь приложение выглядит следующим образом:
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);
};
}
}
Тестирование новой подкоманды
Теперь при попытке запустить приложение без указания подкоманды вы получите сообщение об ошибке, за которым следует справочное сообщение, указывающее доступную подкоманду.
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.
Текст справки для подкоманды read
показывает, что доступны четыре варианта. В нем отображаются допустимые значения для перечисления.
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
Выполните подкоманду read
, указав только --file
параметр , и вы получите значения по умолчанию для трех других параметров.
scl read --file sampleQuotes.txt
Задержка в 42 миллисекундах на символ по умолчанию приводит к снижению скорости чтения. Вы можете ускорить его, задав --delay
меньшее число.
scl read --file sampleQuotes.txt --delay 0
Для задания цветов текста можно использовать --fgcolor
и --light-mode
:
scl read --file sampleQuotes.txt --fgcolor red --light-mode
Укажите недопустимое значение для --delay
, и вы получите сообщение об ошибке:
scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.
Укажите недопустимое значение для --file
, и вы получите исключение:
scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException:
Could not find file 'C:\bin\Debug\net6.0\nofile'.
Добавление подкоманд и настраиваемая проверка
В этом разделе создается окончательная версия приложения. По завершении приложение будет иметь следующие команды и параметры:
- Команда root с параметром global* с именем
--file
- Команда
quotes
read
команда с параметрами с именами--delay
,--fgcolor
и--light-mode
add
команда с аргументами с именамиquote
иbyline
delete
команда с параметром с именем--search-terms
- Команда
* Глобальный параметр доступен для команды, которой он назначен, и рекурсивно для всех ее подкоманд.
Ниже приведен пример входных данных командной строки, который вызывает каждую из доступных команд со своими параметрами и аргументами:
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"
В файле Program.cs замените код, создающий
--file
параметр, следующим кодом: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); } });
Этот код использует для ParseArgument<T> предоставления пользовательского анализа, проверки и обработки ошибок.
Без этого кода отсутствующие файлы передаются с исключением и трассировкой стека. В этом коде отображается только указанное сообщение об ошибке.
Этот код также задает значение по умолчанию, поэтому он задает значение
isDefault
true
. Если не задано значениеisDefault
true
,parseArgument
делегат не будет вызываться, если для--file
не предоставлены входные данные.После кода, создающего
lightModeOption
, добавьте параметры и аргументы дляadd
команд иdelete
:var searchTermsOption = new Option<string[]>( name: "--search-terms", description: "Strings to search for when deleting entries.") { IsRequired = true, AllowMultipleArgumentsPerToken = true }; var quoteArgument = new Argument<string>( name: "quote", description: "Text of quote."); var bylineArgument = new Argument<string>( name: "byline", description: "Byline of quote.");
Параметр AllowMultipleArgumentsPerToken позволяет опустить
--search-terms
имя параметра при указании элементов в списке после первого. Ниже приведены примеры эквивалентных входных данных из командной строки:scl quotes delete --search-terms David "You can do" scl quotes delete --search-terms David --search-terms "You can do"
Замените код, который создает корневую
read
команду и команду, следующим кодом: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);
Этот код вносит следующие изменения:
Удаляет
--file
параметр изread
команды .Добавляет параметр в
--file
качестве глобального параметра в корневую команду.Создает
quotes
команду и добавляет ее в корневую команду.read
Добавляет команду в команду ,quotes
а не в корневую команду.Создает
add
команды иdelete
и добавляет их вquotes
команду .
Результатом будет следующая иерархия команд:
- Корневая команда
quotes
read
add
delete
Теперь приложение реализует рекомендуемый шаблон, в котором родительская команда (
quotes
) указывает область или группу, а ее дочерние команды (read
,add
,delete
) являются действиями.Глобальные параметры применяются к команде и рекурсивно к подкомандам. Так как
--file
находится в корневой команде, она будет автоматически доступна во всех подкомандах приложения.После кода
SetHandler
добавьте новыйSetHandler
код для новых подкоманд:deleteCommand.SetHandler((file, searchTerms) => { DeleteFromFile(file!, searchTerms); }, fileOption, searchTermsOption); addCommand.SetHandler((file, quote, byline) => { AddToFile(file!, quote, byline); }, fileOption, quoteArgument, bylineArgument);
У подкоманды
quotes
нет обработчика, так как она не является конечной командой. Подкомандыread
,add
иdelete
являются конечными командами вquotes
, иSetHandler
вызывается для каждой из них.Добавьте обработчики для
add
иdelete
.internal static void DeleteFromFile(FileInfo file, string[] searchTerms) { Console.WriteLine("Deleting from file"); File.WriteAllLines( file.FullName, File.ReadLines(file.FullName) .Where(line => searchTerms.All(s => !line.Contains(s))).ToList()); } internal static void AddToFile(FileInfo file, string quote, string byline) { Console.WriteLine("Adding to file"); using StreamWriter? writer = file.AppendText(); writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}"); writer.WriteLine($"{Environment.NewLine}-{byline}"); writer.Flush(); }
Готовое приложение выглядит следующим образом:
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();
}
}
Выполните сборку проекта, а затем выполните следующие команды.
Отправьте несуществующий файл в с --file
read
помощью команды , и вы получите сообщение об ошибке вместо исключения и трассировки стека:
scl quotes read --file nofile
File does not exist
Попробуйте выполнить подкоманду quotes
, и вы получите сообщение с указанием использовать read
, add
или 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.
Выполните подкоманду add
, а затем просмотрите конец текстового файла, чтобы увидеть добавленный текст:
scl quotes add "Hello world!" "Nancy Davolio"
Выполните подкоманду delete
со строками поиска из начала файла, а затем просмотрите начало текстового файла, чтобы увидеть, где текст был удален:
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
Примечание
Если вы работаете в папке bin/debug/net6.0 , в этой папке находится файл с изменениями из add
команд и delete
. Копия файла в папке проекта остается неизменной.
Дальнейшие действия
В этом руководстве вы создали простое приложение командной строки, использующее System.CommandLine
. Дополнительные сведения о библиотеке см. в обзореSystem.CommandLine.