System.CommandLine 2.0.1
System.CommandLine
System.CommandLine provides robust support for command-line parsing, invocation, and shell completions in .NET applications. It supports both POSIX and Windows conventions, making it easy to build professional command-line interfaces.
Getting Started
Basic Command
Here's a simple "Hello World" command-line application:
using System.CommandLine;
RootCommand rootCommand = new("Sample command-line app");
Option<string> nameOption = new("--name", "-n")
{
Description = "Your name"
};
rootCommand.Options.Add(nameOption);
rootCommand.SetAction(parseResult =>
{
string name = parseResult.GetValue(nameOption);
Console.WriteLine($"Hello, {name ?? "World"}!");
});
return rootCommand.Parse(args).Invoke();
In this example, we create a RootCommand, add an option for the user's name, and define an action that prints a greeting. The RootCommand is a special kind of Command that comes with a few predefined behaviors:
- It discovers its name automatically from the currently-running application
- It automatically provides
--helpand--versionoptions and default behaviors for them - It provides a default integration with
dotnet-suggestfor dynamic shell completions
You can always override or customize these behaviors as needed on a RootCommand, or create your own top-level Command instead.
Commands with Arguments
Arguments are values passed directly to commands without option names:
var fileArgument = new Argument<FileInfo>("file")
{
Description = "The file to process"
};
var processCommand = new Command("process", "Process a file");
processCommand.Arguments.Add(fileArgument);
processCommand.SetAction(parseResult =>
{
FileInfo file = parseResult.GetValue(fileArgument);
Console.WriteLine($"Processing {file.FullName}");
});
var rootCommand = new RootCommand();
rootCommand.Subcommands.Add(processCommand);
Options with Default Values
Options can have default values and validation:
var rootCommand = new RootCommand();
var delayOption = new Option<int>("--delay", "-d")
{
Description = "Delay in milliseconds",
DefaultValueFactory = _ => 1000
};
delayOption.Validators.Add(result =>
{
if (result.GetValueOrDefault<int>() < 0)
{
result.AddError("Delay must be non-negative");
}
});
rootCommand.Options.Add(delayOption);
Subcommands
Build complex CLI applications with nested commands:
var rootCommand = new RootCommand("My application");
var configCommand = new Command("config", "Configure the application");
var configSetCommand = new Command("set", "Set a configuration value");
var configGetCommand = new Command("get", "Get a configuration value");
var keyOption = new Option<string>("--key")
{
Description = "Configuration key"
};
var valueOption = new Option<string>("--value")
{
Description = "Configuration value"
};
configSetCommand.Options.Add(keyOption);
configSetCommand.Options.Add(valueOption);
configGetCommand.Options.Add(keyOption);
configCommand.Subcommands.Add(configSetCommand);
configCommand.Subcommands.Add(configGetCommand);
rootCommand.Subcommands.Add(configCommand);
// Usage: myapp config set --key "apiUrl" --value "https://api.example.com"
// Usage: myapp config get --key "apiUrl"
Using Options in Command Actions
Access option values through the ParseResult:
var connectionOption = new Option<string>("--connection")
{
Description = "Database connection string"
};
var timeoutOption = new Option<int>("--timeout")
{
Description = "Timeout in seconds",
DefaultValueFactory = _ => 30
};
var verboseOption = new Option<bool>("--verbose")
{
Description = "Enable verbose output"
};
rootCommand.Options.Add(connectionOption);
rootCommand.Options.Add(timeoutOption);
rootCommand.Options.Add(verboseOption);
rootCommand.SetAction(parseResult =>
{
var connection = parseResult.GetValue(connectionOption);
var timeout = parseResult.GetValue(timeoutOption);
var verbose = parseResult.GetValue(verboseOption);
Console.WriteLine($"Connection: {connection}");
Console.WriteLine($"Timeout: {timeout}");
Console.WriteLine($"Verbose: {verbose}");
});
Shell Completions
Enable tab completion for your CLI:
// Completions are automatically available for all commands, options, and arguments
var rootCommand = new RootCommand("My app with completions");
var fileOption = new Option<FileInfo>("--file")
{
Description = "The file to process"
};
// Add custom completions using CompletionSources
fileOption.CompletionSources.Add(ctx =>
// hard-coded list of files
["file1.txt", "file2.txt", "file3.txt" ]
);
// Or add simple string suggestions
fileOption.CompletionSources.Add("option1", "option2", "option3");
rootCommand.Options.Add(fileOption);
Users can then easily trigger your completions using dotnet-suggest:
> dotnet tool install -g dotnet-suggest
> dotnet suggest script bash > ~/.bashrc
> dotnet tool install -g dotnet-suggest
> dotnet suggest script powershell >> $PROFILE
Once dotnet-suggest is installed, you can register your app with it for completions support:
> dotnet-suggest register --command-path /path/to/myapp
Alternatively, you can create your own commands for completion generation and instruct users on how to set them up.
Async Command Handlers
Support for asynchronous operations:
var urlOption = new Option<string>("--url")
{
Description = "The URL to fetch"
};
rootCommand.Options.Add(urlOption);
rootCommand.SetAction(async (parseResult, cancellationToken) =>
{
var url = parseResult.GetValue(urlOption);
if (url != null)
{
using var client = new HttpClient();
var response = await client.GetStringAsync(url, cancellationToken);
Console.WriteLine(response);
}
});
// Or return an exit code:
rootCommand.SetAction(async (parseResult, cancellationToken) =>
{
// Your async logic here
return await Task.FromResult(0); // Return exit code
});
Notable Changes Since v2.0.0-beta7
New Features
- Improved Help System: Enhanced
HelpActionto allow users to provide customMaxWidthfor help text formatting (#2635). Note that if you create custom Help or Version actions, you'll want to setClearsParseErrorstotrueto ensure that invoking those features isn't treated like an error by the parser. Task<int>Support: AddedSetActionoverload forTask<int>return types (#2634)- Detect Implicit Arguments: Added the
ArgumentResult.Implicitproperty for better argument handling (#2622, #2625) - Performance Improvements: Reduced reflection usage throughout the library for better performance (#2662)
Bug Fixes
- Fixed issue #2128: Resolved command parsing edge cases (#2656)
- Fixed issue #2257: Corrected argument validation behavior
- Fixed issue #2589: Improved error message clarity (#2654)
- Fixed issue #2591: Resolved option parsing inconsistencies (#2644)
- Fixed issue #2622: Enhanced implicit argument support (#2625)
- Fixed issue #2628: Corrected help text formatting issues
- Fixed issue #2634: Added missing Task
action support - Fixed issue #2640: Resolved completion suggestions for nested commands (#2646)
Breaking Changes
- Default value handling for
ProcessTerminationTimeouthas been re-added (#2672) - Some internal APIs have been refactored to reduce reflection usage (#2662)
Other Improvements
- Updated to .NET 10.0 RC1 compatibility
- Improved memory usage and performance optimizations
- Better handling of complex command hierarchies
Documentation
For comprehensive documentation, tutorials, and API reference, visit:
- Microsoft Learn Documentation - Complete guides and API reference
- GitHub Repository - Source code, samples, and issues
Framework Support
- .NET 8.0+ - Full feature support with trimming and AOT compilation
- .NET Standard 2.0 - Compatible with .NET Framework 4.6.1+, .NET Core 2.0+
License
This package is licensed under the MIT License.
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
No packages depend on System.CommandLine.
| Version | Downloads | Last updated |
|---|---|---|
| 2.0.1 | 7 | 12/11/2025 |
| 2.0.0 | 1 | 12/12/2025 |
| 2.0.0-rc.2.25502.107 | 1 | 12/12/2025 |
| 2.0.0-rc.1.25451.107 | 64 | 12/10/2025 |
| 2.0.0-beta7.25380.108 | 52 | 12/10/2025 |
| 2.0.0-beta6.25358.103 | 2 | 12/12/2025 |
| 2.0.0-beta5.25306.1 | 1 | 12/12/2025 |
| 2.0.0-beta4.22272.1 | 2 | 12/12/2025 |
| 2.0.0-beta3.22114.1 | 1 | 12/12/2025 |
| 2.0.0-beta3.22111.2 | 2 | 12/12/2025 |
| 2.0.0-beta3.22106.2 | 1 | 12/12/2025 |
| 2.0.0-beta3.22103.3 | 1 | 12/12/2025 |
| 2.0.0-beta2.21617.1 | 2 | 12/12/2025 |
| 2.0.0-beta1.21308.1 | 3 | 12/11/2025 |
| 2.0.0-beta1.21216.1 | 2 | 12/12/2025 |
| 2.0.0-beta1.20574.7 | 3 | 12/11/2025 |
| 2.0.0-beta1.20371.2 | 3 | 12/12/2025 |
| 2.0.0-beta1.20214.1 | 2 | 12/12/2025 |
| 2.0.0-beta1.20213.1 | 2 | 12/12/2025 |
| 2.0.0-beta1.20158.1 | 2 | 12/12/2025 |
| 2.0.0-beta1.20104.2 | 2 | 12/12/2025 |
| 2.0.0-beta1.20071.2 | 2 | 12/12/2025 |
| 0.3.0-alpha.20070.2 | 1 | 12/12/2025 |
| 0.3.0-alpha.20054.1 | 2 | 12/12/2025 |