|
| 1 | +# System.CommandLine |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +## Getting Started |
| 6 | + |
| 7 | +### Basic Command |
| 8 | + |
| 9 | +Here's a simple "Hello World" command-line application: |
| 10 | + |
| 11 | +```csharp |
| 12 | +using System.CommandLine; |
| 13 | + |
| 14 | +var rootCommand = new RootCommand("Sample command-line app"); |
| 15 | + |
| 16 | +var nameOption = new Option<string>( |
| 17 | + aliases: ["--name", "-n"], |
| 18 | + description: "Your name"); |
| 19 | + |
| 20 | +rootCommand.Options.Add(nameOption); |
| 21 | + |
| 22 | +rootCommand.SetAction(parseResult => |
| 23 | +{ |
| 24 | + string name = parseResult.GetValueForOption(nameOption); |
| 25 | + Console.WriteLine($"Hello, {name ?? "World"}!"); |
| 26 | +}); |
| 27 | + |
| 28 | +return await rootCommand.InvokeAsync(args); |
| 29 | +``` |
| 30 | + |
| 31 | +### Commands with Arguments |
| 32 | + |
| 33 | +Arguments are values passed directly to commands without option names: |
| 34 | + |
| 35 | +```csharp |
| 36 | +var fileArgument = new Argument<FileInfo>( |
| 37 | + name: "file", |
| 38 | + description: "The file to process"); |
| 39 | + |
| 40 | +var processCommand = new Command("process", "Process a file"); |
| 41 | +processCommand.Arguments.Add(fileArgument); |
| 42 | + |
| 43 | +processCommand.SetAction(parseResult => |
| 44 | +{ |
| 45 | + FileInfo file = parseResult.GetValueForArgument(fileArgument); |
| 46 | + Console.WriteLine($"Processing {file.FullName}"); |
| 47 | +}); |
| 48 | + |
| 49 | +rootCommand.Subcommands.Add(processCommand); |
| 50 | +``` |
| 51 | + |
| 52 | +### Options with Default Values |
| 53 | + |
| 54 | +Options can have default values and validation: |
| 55 | + |
| 56 | +```csharp |
| 57 | +var delayOption = new Option<int>( |
| 58 | + aliases: ["--delay", "-d"], |
| 59 | + getDefaultValue: () => 1000, |
| 60 | + description: "Delay in milliseconds"); |
| 61 | + |
| 62 | +delayOption.AddValidator(result => |
| 63 | +{ |
| 64 | + if (result.GetValueOrDefault<int>() < 0) |
| 65 | + { |
| 66 | + result.ErrorMessage = "Delay must be non-negative"; |
| 67 | + } |
| 68 | +}); |
| 69 | + |
| 70 | +rootCommand.Options.Add(delayOption); |
| 71 | +``` |
| 72 | + |
| 73 | +### Subcommands |
| 74 | + |
| 75 | +Build complex CLI applications with nested commands: |
| 76 | + |
| 77 | +```csharp |
| 78 | +var rootCommand = new RootCommand("My application"); |
| 79 | + |
| 80 | +var configCommand = new Command("config", "Configure the application"); |
| 81 | +var configSetCommand = new Command("set", "Set a configuration value"); |
| 82 | +var configGetCommand = new Command("get", "Get a configuration value"); |
| 83 | + |
| 84 | +var keyOption = new Option<string>("--key", "Configuration key"); |
| 85 | +var valueOption = new Option<string>("--value", "Configuration value"); |
| 86 | + |
| 87 | +configSetCommand.Options.Add(keyOption); |
| 88 | +configSetCommand.Options.Add(valueOption); |
| 89 | +configGetCommand.Options.Add(keyOption); |
| 90 | + |
| 91 | +configCommand.Subcommands.Add(configSetCommand); |
| 92 | +configCommand.Subcommands.Add(configGetCommand); |
| 93 | +rootCommand.Subcommands.Add(configCommand); |
| 94 | + |
| 95 | +// Usage: myapp config set --key "apiUrl" --value "https://api.example.com" |
| 96 | +// Usage: myapp config get --key "apiUrl" |
| 97 | +``` |
| 98 | + |
| 99 | +### Using Options in Command Handlers |
| 100 | + |
| 101 | +Access option values through the ParseResult: |
| 102 | + |
| 103 | +```csharp |
| 104 | +var connectionOption = new Option<string>("--connection", "Database connection string"); |
| 105 | +var timeoutOption = new Option<int>("--timeout", getDefaultValue: () => 30); |
| 106 | +var verboseOption = new Option<bool>("--verbose"); |
| 107 | + |
| 108 | +rootCommand.Options.Add(connectionOption); |
| 109 | +rootCommand.Options.Add(timeoutOption); |
| 110 | +rootCommand.Options.Add(verboseOption); |
| 111 | + |
| 112 | +rootCommand.SetAction(parseResult => |
| 113 | +{ |
| 114 | + var connection = parseResult.GetValueForOption(connectionOption); |
| 115 | + var timeout = parseResult.GetValueForOption(timeoutOption); |
| 116 | + var verbose = parseResult.GetValueForOption(verboseOption); |
| 117 | + |
| 118 | + Console.WriteLine($"Connection: {connection}"); |
| 119 | + Console.WriteLine($"Timeout: {timeout}"); |
| 120 | + Console.WriteLine($"Verbose: {verbose}"); |
| 121 | +}); |
| 122 | +``` |
| 123 | + |
| 124 | +### Shell Completions |
| 125 | + |
| 126 | +Enable tab completion for your CLI: |
| 127 | + |
| 128 | +```csharp |
| 129 | +// Completions are automatically available for all commands, options, and arguments |
| 130 | +var rootCommand = new RootCommand("My app with completions"); |
| 131 | + |
| 132 | +var fileOption = new Option<FileInfo>("--file", "The file to process"); |
| 133 | +fileOption.AddCompletions((ctx) => |
| 134 | +{ |
| 135 | + // Custom completion logic |
| 136 | + return new[] { "file1.txt", "file2.txt", "file3.txt" }; |
| 137 | +}); |
| 138 | + |
| 139 | +rootCommand.Options.Add(fileOption); |
| 140 | + |
| 141 | +// Users can generate completion scripts using dotnet-suggest: |
| 142 | +// dotnet tool install -g dotnet-suggest |
| 143 | +// dotnet suggest script bash > ~/.bashrc |
| 144 | +// dotnet suggest script powershell > $PROFILE |
| 145 | +``` |
| 146 | + |
| 147 | +### Async Command Handlers |
| 148 | + |
| 149 | +Support for asynchronous operations: |
| 150 | + |
| 151 | +```csharp |
| 152 | +var urlOption = new Option<string>("--url", "The URL to fetch"); |
| 153 | +rootCommand.Options.Add(urlOption); |
| 154 | + |
| 155 | +rootCommand.SetAction(async (parseResult, cancellationToken) => |
| 156 | +{ |
| 157 | + var url = parseResult.GetValueForOption(urlOption); |
| 158 | + if (url != null) |
| 159 | + { |
| 160 | + using var client = new HttpClient(); |
| 161 | + var response = await client.GetStringAsync(url, cancellationToken); |
| 162 | + Console.WriteLine(response); |
| 163 | + } |
| 164 | +}); |
| 165 | + |
| 166 | +// Or return an exit code: |
| 167 | +rootCommand.SetAction(async (parseResult, cancellationToken) => |
| 168 | +{ |
| 169 | + // Your async logic here |
| 170 | + return await Task.FromResult(0); // Return exit code |
| 171 | +}); |
| 172 | +``` |
| 173 | + |
| 174 | +## Notable Changes Since v2.0.0-beta7 |
| 175 | + |
| 176 | +### New Features |
| 177 | +- **Finnish Localization**: Added Finnish language translations for help text and error messages ([#2605](https://github.com/dotnet/command-line-api/pull/2605)) |
| 178 | +- **Improved Help System**: Enhanced `HelpAction` to allow users to provide custom `MaxWidth` for help text formatting ([#2635](https://github.com/dotnet/command-line-api/pull/2635)) |
| 179 | +- **Task<int> Support**: Added `SetAction` overload for `Task<int>` return types ([#2634](https://github.com/dotnet/command-line-api/issues/2634)) |
| 180 | +- **Implicit Arguments**: Added `ArgumentResult.Implicit` property for better argument handling ([#2622](https://github.com/dotnet/command-line-api/issues/2622), [#2625](https://github.com/dotnet/command-line-api/pull/2625)) |
| 181 | +- **Performance Improvements**: Reduced reflection usage throughout the library for better performance ([#2662](https://github.com/dotnet/command-line-api/pull/2662)) |
| 182 | + |
| 183 | +### Bug Fixes |
| 184 | +- Fixed issue [#2128](https://github.com/dotnet/command-line-api/issues/2128): Resolved command parsing edge cases ([#2656](https://github.com/dotnet/command-line-api/pull/2656)) |
| 185 | +- Fixed issue [#2257](https://github.com/dotnet/command-line-api/issues/2257): Corrected argument validation behavior |
| 186 | +- Fixed issue [#2589](https://github.com/dotnet/command-line-api/issues/2589): Improved error message clarity ([#2654](https://github.com/dotnet/command-line-api/pull/2654)) |
| 187 | +- Fixed issue [#2591](https://github.com/dotnet/command-line-api/issues/2591): Resolved option parsing inconsistencies ([#2644](https://github.com/dotnet/command-line-api/pull/2644)) |
| 188 | +- Fixed issue [#2622](https://github.com/dotnet/command-line-api/issues/2622): Enhanced implicit argument support ([#2625](https://github.com/dotnet/command-line-api/pull/2625)) |
| 189 | +- Fixed issue [#2628](https://github.com/dotnet/command-line-api/issues/2628): Corrected help text formatting issues |
| 190 | +- Fixed issue [#2634](https://github.com/dotnet/command-line-api/issues/2634): Added missing Task<int> action support |
| 191 | +- Fixed issue [#2640](https://github.com/dotnet/command-line-api/issues/2640): Resolved completion suggestions for nested commands ([#2646](https://github.com/dotnet/command-line-api/pull/2646)) |
| 192 | + |
| 193 | +### Breaking Changes |
| 194 | +- Default value handling for `ProcessTerminationTimeout` has been re-added ([#2672](https://github.com/dotnet/command-line-api/pull/2672)) |
| 195 | +- Some internal APIs have been refactored to reduce reflection usage ([#2662](https://github.com/dotnet/command-line-api/pull/2662)) |
| 196 | + |
| 197 | +### Other Improvements |
| 198 | +- Updated to .NET 10.0 RC1 compatibility |
| 199 | +- Enhanced parsing logic for better POSIX and Windows convention support |
| 200 | +- Improved memory usage and performance optimizations |
| 201 | +- Better handling of complex command hierarchies |
| 202 | + |
| 203 | +## Documentation |
| 204 | + |
| 205 | +For comprehensive documentation, tutorials, and API reference, visit: |
| 206 | +- **[Microsoft Learn Documentation](https://learn.microsoft.com/en-us/dotnet/standard/commandline/)** - Complete guides and API reference |
| 207 | +- **[GitHub Repository](https://github.com/dotnet/command-line-api)** - Source code, samples, and issues |
| 208 | +- **[Sample Applications](https://github.com/dotnet/command-line-api/tree/main/samples)** - Real-world examples |
| 209 | + |
| 210 | +## Framework Support |
| 211 | + |
| 212 | +- **.NET 8.0+** - Full feature support with trimming and AOT compilation |
| 213 | +- **.NET Standard 2.0** - Compatible with .NET Framework 4.6.1+, .NET Core 2.0+ |
| 214 | + |
| 215 | +## License |
| 216 | + |
| 217 | +This package is licensed under the [MIT License](https://opensource.org/licenses/MIT). |
| 218 | + |
| 219 | +## Contributing |
| 220 | + |
| 221 | +We welcome contributions! Please see our [Contributing Guide](https://github.com/dotnet/command-line-api/blob/main/CONTRIBUTING.md) for details. |
| 222 | + |
| 223 | +## Support |
| 224 | + |
| 225 | +- **Issues**: [GitHub Issues](https://github.com/dotnet/command-line-api/issues) |
| 226 | +- **Discussions**: [GitHub Discussions](https://github.com/dotnet/command-line-api/discussions) |
| 227 | +- **Chat**: [Gitter Community](https://gitter.im/dotnet/command-line-api) |
0 commit comments