PRI.CommandLineExtensions
1.0.4
dotnet add package PRI.CommandLineExtensions --version 1.0.4
NuGet\Install-Package PRI.CommandLineExtensions -Version 1.0.4
<PackageReference Include="PRI.CommandLineExtensions" Version="1.0.4" />
<PackageVersion Include="PRI.CommandLineExtensions" Version="1.0.4" />
<PackageReference Include="PRI.CommandLineExtensions" />
paket add PRI.CommandLineExtensions --version 1.0.4
#r "nuget: PRI.CommandLineExtensions, 1.0.4"
#addin nuget:?package=PRI.CommandLineExtensions&version=1.0.4
#tool nuget:?package=PRI.CommandLineExtensions&version=1.0.4
System.CommandLine Extensions
CommandLineExtensions helps make command line argument parsing simple while extending and leveraging Microsoft-supported System.CommandLine
.
Mission
To leverage ConsoleApplicationBuilder to create a simple way to define and process command-line parameters with a fluent interface.
Designed with ConsoleApplicationBuilder in mind.
ConsoleApplicationBuilder helps create .NET console apps that support configuration, logging, Dependency Injection (DI), lifetime, and other things that .NET developers have come to expect.
var builder = ConsoleApplication.CreateBuilder(args); // đđŊ ConsoleApplicationBuilder
builder.AddCommand() // đđŊ CommandLineExtensions start here, adding a root command-line command
.WithDescription("File processor") // đđŊ with a description of "đđĒđđĻ đąđŗđ°đ¤đĻđ´đ´đ°đŗ"
.WithArgument("file", "file path to process") // đđŊ with a required argument named "đ§đĒđđĻ" and described "đ§đĒđđĻ đąđĸđĩđŠ đĩđ° đąđŗđ°đ¤đĻđ´đ´"
.WithHandler((file)=>{}); // đđŊ with a handler to invoke when executed
return builder.Build<RootCommand>().Invoke(args);
Benefits
- Simplicity.
- Fluent Interface.
- Explicitness.
- Reduced gotchas.
More Examples
Required Named Options
Required options can be added with the WithRequiredOption
method. For example:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand<ProcessFileCommand>()
.WithRequiredOption<FileInfo>("--file", "file path to process") // đđŊ required option
.WithHandler(fileInfo =>
{
Console.WriteLine($"File {fileInfo.Name} is {fileInfo.Length} bytes in size and was created on {fileInfo.CreationTime}.");
});
return builder.Build<ProcessFileCommand>().Invoke(args);
The command-line for this example might be:
app --file file.txt
The help for the above example is:
Description:
File processor
Usage:
app [options]
Options:
--file <file> (REQUIRED) file path to process
--version Show version information
-?, -h, --help Show help and usage information
Injection
System.CommandLine does not inherently support .NET dependency injection. CommandLineExtensions enables dependency injection via ConsoleApplicationBuilder. Commands added through the AddCommand
extension method automatically leverage dependency injection and any types that are instantiated by CommandLineExtensions will have dependencies resolved. For example:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand<ProcessFileCommand>() // đđŊ ProcessFileCommand depends on ILogger<T> injected via the constructor.
.WithArgument<FileInfo>("file", "The filename to process.")
.WithHandler<ProcessFileCommandHandler>();
return builder.Build<ProcessFileCommand>().Invoke(args);
// logger will be injected when ProcessFileCommand is built đđŊ
public class ProcessFileCommandHandler(ILogger<ProcessFileCommandHandler> logger)
: ICommandHandler<FileInfo>
{
public int Execute(FileInfo fileInfo)
{
logger.LogInformation("Executed called.");
Console.WriteLine($"Got parameter '{fileInfo.FullName}");
return 0;
}
}
public class ProcessFileCommand() : RootCommand("File processor");
Enum
Options
An option with a fixed, finite set of values can easily be supported with an enum
. Verbosity, for example:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand<ProcessFileCommand>()
.WithOption<Verbosities>("--verbosity", "verbosity") // đđŊ Verbosities enum
.WithHandler(v =>
{
switch (v)
{
case Verbosities.Information:
StdOut.WriteLine("Something happened.");
break;
case Verbosities.Warning:
StdOut.WriteLine("Something bad happened.");
break;
case Verbosities.Error:
StdOut.WriteLine("Error happened.");
break;
case Verbosities.Fatal:
StdOut.WriteLine("Something fatal happened!");
break;
case Verbosities.Quiet:
default:
break;
}
actualVerbosity = v;
});
var command = builder.Build<ProcessFileCommand>();
return command.Invoke(["--help"]);
public enum Verbosities
{
Quiet,
Information,
Warning,
Error,
Fatal
}
And will produce help output similar to:
Description:
File processor
Usage:
app [options]
Options:
--verbosity <Error|Fatal|Information|Quiet|Warning> verbosity
--version Show version information
-?, -h, --help Show help and usage information
Option Aliases
Options require a name used to specify the option on the command line. Additional names (aliases) can be configured for a single option. For example, an option with the name --option
can have an alias named -o
:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand<ProcessFileCommand>()
.WithArgument<FileInfo>("file", "file path to process")
.WithOption<FileInfo?>("--output", "output location")
.AddAlias("-o") // đđŊ add alias '-o' for '--output'
.WithHandler((inputFileInfo, outputFileInfo) =>
{
var outputText =
$"File {inputFileInfo.Name} is {inputFileInfo.Length} bytes in size and was created on {inputFileInfo.CreationTime}";
if (outputFileInfo != null)
{
using var writer = outputFileInfo.CreateText();
writer.WriteLine(outputText);
}
else
{
Console.WriteLine(outputText);
}
});
return builder.Build<ProcessFileCommand>().Invoke(args);
Subcommands
System.CommandLine commands can have multiple subcommands so that when the application is executed the command line arguments define which subcommand to execute. For example, an application can have two subcommands, one to read
a file and one to write
a file:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand()
.WithSubcommand<ReadCommand>() // đđŊ add read command
.WithArgument<FileInfo>("file", "file path to process")
.WithSubcommandHandler(file => Console.WriteLine($"read <file> argument = {file.Name}"))
.WithSubcommand<WriteCommand>() // đđŊ add write command
.WithArgument<FileInfo>("file", "file path to process")
.WithArgument<string>("text", "text to write to file, quoted.")
.WithSubcommandHandler((file, text) => Console.WriteLine($"write <file> argument = {file.Name} with text '{text}'."))
.WithHandler(() => Console.WriteLine("Please choose read or write subcommand."));
return builder.Build<RootCommand>().Invoke(args, Console);
public class ReadCommand() : Command("read", "read subcommand");
public class WriteCommand() : Command("write", "write subcommand");
Subcommand Aliases
Like options, subcommands may have aliases. For example, the above read/write subcommands example could have aliases r
and w
for read
and write
:
var builder = ConsoleApplication.CreateBuilder(args);
builder.Services.AddCommand()
.WithSubcommand<ReadCommand>()
.AddAlias("r") // đđŊ add alias "r" for read command
.WithArgument<FileInfo>("file", "file path to process")
.WithSubcommandHandler(file => Console.WriteLine($"read <file> argument = {file.Name}"))
.WithSubcommand<WriteCommand>()
.AddAlias("w") // đđŊ add alias "w" for write command
.WithArgument<FileInfo>("file", "file path to process")
.WithArgument<string>("text", "text to write to file, quoted.")
.WithSubcommandHandler((file, text) => Console.WriteLine($"write <file> argument = {file.Name} with text '{text}'."))
.WithHandler(() => Console.WriteLine("Please choose read or write subcommand."));
return builder.Build<RootCommand>().Invoke(args, Console);
Typed command handlers
One of the intents of CommandLineExtensions is to help make complex command-line applications simpler. Lambdas make for self-contained, easy-to-read examples; but they're not indicative of real-world, non-trivial applications. Complex applications often use tried-and-true object-oriented techniques to address complexity. Techniques like type encapsulation are used to encapsulate specific data and logic from other concerns of the application. A command handler, for example, may be encapsulated in a class declaration. CommandLineExtensions supports types that implement built-in interface Pri.CommandLineExtensions.ICommandHandler
.
Limitations and known issues
This is the first, MVP release of CommandLineExtensions. It has some limitations and doesn't support all the features of System.CommandLine. CommandLineExtensions is intended to be simpler than System.CommandLine extensions and tries to address the most common command-line arguments/commands needs and is unlikely to ever have parity with System.CommandLine. Here are some of the limitations and known issues with CommandLineExtensions:
FromAmong
isn't directly supported, use theenum
support instead.- Parameters (arguments, options) are limited to two. Support for more parameters is planned for the future.
- There is no support for
Option<T>
-derived types. CommandLineExtensions focuses on a fluent interface, but support for this may be added in the future. - Default value factories are not supported, only compile-time constants or config-time values. Support for default value factories in System.CommandLine is more complex than it's worth (requiring custom help messages, etc.)
- Global options are not yet supported.
- Custom help is not supported.
- Unmatched tokens is not supported.
Support
For support, please see the GitHub Issues section of ConsoleApplicationBuilder.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- System.CommandLine (>= 2.0.0-beta4.22272.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.