A simple, lightweight, and dependency-free command-line parsing library for .NET.
| Feature | MiniCommandLineParser | Other Libraries |
|---|---|---|
| Zero Dependencies | β No external packages | β Often require multiple dependencies |
| Lightweight | β Minimal footprint | β Can be bloated |
| Bidirectional | β Parse & Format | β Usually parse-only |
| Learning Curve | β Simple, intuitive API | β Complex configurations |
| Multi-target | β .NET 6/7/8/9 + Standard 2.1 |
- πͺΆ Lightweight - Minimal footprint with zero external dependencies
- π― Simple API - Intuitive attribute-based configuration
- π¦ Multi-target - Supports .NET 6.0, 7.0, 8.0, 9.0 and .NET Standard 2.1
- π Bidirectional - Parse arguments to objects AND format objects back to command-line strings
- π Auto Help Text - Built-in help text generation with customizable formatting
- π§ Flexible - Supports short/long options, positional arguments, arrays, enums, flags, and more
- π§΅ Thread-Safe - Type information caching is thread-safe for concurrent usage
- β‘ High Performance - Efficient parsing with minimal allocations
- π Positional Arguments - Support for index-based arguments like
git clone <url> - π Custom Separators - Define custom separators for array values (e.g.,
--tags=a;b;c) - β
Boolean Flexibility - Multiple syntaxes:
--flag,--flag=true,--flag true
Install-Package MiniCommandLineParserdotnet add package MiniCommandLineParser<PackageReference Include="MiniCommandLineParser" Version="*" />using MiniCommandLineParser;
public class Options
{
[Option('i', "input", Required = true, HelpText = "Input file path")]
public string InputFile { get; set; }
[Option('o', "output", HelpText = "Output file path")]
public string OutputFile { get; set; }
[Option('v', "verbose", HelpText = "Enable verbose output")]
public bool Verbose { get; set; }
[Option("count", HelpText = "Number of iterations")]
public int Count { get; set; } = 1;
}using MiniCommandLineParser;
class Program
{
static void Main(string[] args)
{
var result = Parser.Default.Parse<Options>(args);
if (result.Result == ParserResultType.Parsed)
{
var options = result.Value;
Console.WriteLine($"Input: {options.InputFile}");
Console.WriteLine($"Output: {options.OutputFile}");
Console.WriteLine($"Verbose: {options.Verbose}");
Console.WriteLine($"Count: {options.Count}");
}
else
{
Console.WriteLine($"Error: {result.ErrorMessage}");
}
}
}myapp --input data.txt --output result.txt --verbose --count 5
# or using short names
myapp -i data.txt -o result.txt -v --count 5The [Option] attribute is used to mark properties as command-line options:
public class Options
{
// Short name only (property name becomes long name)
[Option('v')]
public bool Verbose { get; set; }
// Long name only
[Option("configuration")]
public string Config { get; set; }
// Both short and long names
[Option('o', "output")]
public string OutputPath { get; set; }
// No parameters (property name becomes long name)
[Option]
public string DefaultName { get; set; }
// Required option
[Option('i', "input", Required = true, HelpText = "Required input file")]
public string Input { get; set; }
// With help text
[Option("timeout", HelpText = "Timeout in seconds")]
public int Timeout { get; set; } = 30;
// Positional argument (Index >= 0 makes it positional)
[Option(Index = 0, MetaName = "COMMAND", HelpText = "The command to execute")]
public string Command { get; set; }
// Array with custom separator
[Option("tags", Separator = ';', HelpText = "Tags separated by semicolon")]
public List<string> Tags { get; set; }
}MiniCommandLineParser supports various argument formats:
# Short options
-v -i input.txt
# Long options
--verbose --input input.txt
# Equals syntax
--input=input.txt
-i=input.txt
# Quoted values (for paths with spaces)
--output "my output file.txt"
--path="C:\Program Files\MyApp"
# Boolean flags - multiple equivalent syntaxes
--verbose # Sets Verbose = true (flag presence)
--verbose=true # Sets Verbose = true (equals syntax)
--verbose true # Sets Verbose = true (space syntax)
--verbose=false # Sets Verbose = false
--verbose false # Sets Verbose = false
-v # Short form, sets Verbose = true
-v=true # Short form with equals
-v false # Short form with space
# Positional arguments (no option prefix needed)
myapp clone http://example.com --verbose
# Where 'clone' is Index=0 and 'http://example.com' is Index=1
# Array with custom separator (using equals syntax)
--tags=dev;test;prod # Parsed using ';' separator
--ids=1,2,3,4 # Parsed using ',' separatorSupport for collection types like List<T> and arrays:
public class Options
{
[Option("files", HelpText = "Input files to process")]
public List<string> Files { get; set; }
[Option("numbers", HelpText = "Numbers to sum")]
public int[] Numbers { get; set; }
// With custom separator - elements can be passed in a single value
[Option("tags", Separator = ';', HelpText = "Tags separated by semicolon")]
public List<string> Tags { get; set; }
[Option("ids", Separator = ',', HelpText = "IDs separated by comma")]
public List<int> Ids { get; set; }
}Usage:
# Space-separated (default)
myapp --files file1.txt file2.txt file3.txt --numbers 1 2 3 4 5
# Using custom separator with equals syntax
myapp --tags=dev;test;prod --ids=1,2,3,4,5
# Mixed: separator works with both syntaxes
myapp --tags dev;test;prod
myapp --tags "dev;test;prod"Both regular enums and flags enums are supported:
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}
[Flags]
public enum Features
{
None = 0,
Logging = 1,
Caching = 2,
Compression = 4,
All = Logging | Caching | Compression
}
public class Options
{
[Option("level", HelpText = "Log level")]
public LogLevel Level { get; set; } = LogLevel.Info;
[Option("features", HelpText = "Enabled features")]
public Features EnabledFeatures { get; set; }
}Usage:
# Regular enum
myapp --level Warning
# Flags enum (multiple values)
myapp --features Logging Caching CompressionPositional arguments are values that don't require an option prefix. They are identified by their position in the command line, similar to how git clone <url> works.
public class GitCloneOptions
{
// Index = 0 means this is the first positional argument
[Option(Index = 0, MetaName = "COMMAND", HelpText = "Git command to execute")]
public string Command { get; set; } = "";
// Index = 1 means this is the second positional argument
[Option(Index = 1, MetaName = "URL", HelpText = "Repository URL to clone")]
public string Url { get; set; } = "";
// Regular named options still work alongside positional arguments
[Option('v', "verbose", HelpText = "Enable verbose output")]
public bool Verbose { get; set; }
[Option("depth", HelpText = "Create a shallow clone with specified depth")]
public int Depth { get; set; }
}Usage:
# Positional arguments come first (or can be mixed with named options)
myapp clone https://github.com/user/repo.git --verbose --depth 1
# Named options can appear before positional arguments too
myapp --verbose clone https://github.com/user/repo.git --depth 1
# The parser correctly identifies positional vs named argumentsKey Points:
- Use
Indexproperty to define positional arguments (Index >= 0) MetaNameprovides a display name for help text (e.g., "URL" instead of the property name)- Positional arguments are matched in order by their Index value
- Named options (with
-or--prefix) can appear anywhere in the command line - Positional and named options can be freely mixed
| Type | Example | Notes |
|---|---|---|
string |
--name "John Doe" |
Supports quoted values |
int, long, short |
--count 42 |
All integer types |
float, double, decimal |
--rate 3.14 |
Floating-point types |
bool |
--verbose or --flag=true |
Flag presence = true; also supports --flag true |
enum |
--level Info |
Case-insensitive by default |
[Flags] enum |
--flags A B C |
Multiple space-separated values |
List<T>, T[] |
--items a b c |
Any supported element type |
| Arrays with separator | --tags=a;b;c |
Use Separator property in attribute |
var parser = new Parser(new ParserSettings
{
// Case-sensitive option matching (default: false)
CaseSensitive = false,
// Ignore unknown arguments (default: true)
IgnoreUnknownArguments = true
});
var result = parser.Parse<Options>(args);| Setting | Default | Description |
|---|---|---|
CaseSensitive |
false |
When false, --Input matches --input |
IgnoreUnknownArguments |
true |
When true, unknown args are silently ignored |
A unique feature of MiniCommandLineParser is the ability to convert options objects back to command-line strings:
var options = new Options
{
InputFile = "data.txt",
OutputFile = "result.txt",
Verbose = true,
Count = 5
};
// Complete format - includes all options (space-separated style)
string complete = Parser.FormatCommandLine(options, CommandLineFormatMethod.Complete);
// Output: --input data.txt --output result.txt --verbose True --count 5
// Simplified format - only non-default values
string simplified = Parser.FormatCommandLine(options, CommandLineFormatMethod.Simplify);
// Output: --input data.txt --output result.txt --verbose True --count 5
// Equal sign style - uses --option=value syntax
string equalStyle = Parser.FormatCommandLine(options, CommandLineFormatMethod.EqualSignStyle);
// Output: --input=data.txt --output=result.txt --verbose=True --count=5
// Combine flags: Simplify + EqualSignStyle
string combined = Parser.FormatCommandLine(options,
CommandLineFormatMethod.Simplify | CommandLineFormatMethod.EqualSignStyle);
// Output: --input=data.txt --output=result.txt --verbose=True --count=5
// Get as string array
string[] args = Parser.FormatCommandLineArgs(options, CommandLineFormatMethod.Simplify);Arrays are formatted differently based on the style:
public class BuildOptions
{
[Option("tags", Separator = ';')]
public List<string> Tags { get; set; }
[Option("ids", Separator = ',')]
public int[] Ids { get; set; }
}
var options = new BuildOptions
{
Tags = new List<string> { "dev", "test", "prod" },
Ids = new[] { 1, 2, 3 }
};
// Space-separated style (Complete/Simplify without EqualSignStyle)
Parser.FormatCommandLine(options, CommandLineFormatMethod.Complete);
// Output: --tags dev test prod --ids 1 2 3
// Equal sign style - arrays automatically use their defined separator
Parser.FormatCommandLine(options, CommandLineFormatMethod.EqualSignStyle);
// Output: --tags=dev;test;prod --ids=1,2,3| Flag | Description |
|---|---|
None |
Default space-separated style |
Complete |
Output all options including default values |
Simplify |
Only output options that differ from defaults |
EqualSignStyle |
Use --option=value syntax; arrays use their defined separator |
Note: Flags can be combined using the
|operator for flexible output formatting.
- Configuration persistence: Save/restore command-line configurations
- Process spawning: Launch child processes with the same options
- Logging/Debugging: Log the effective command-line for diagnostics
- Testing: Generate test command lines programmatically
var options = new Options();
string helpText = Parser.GetHelpText(options);
Console.WriteLine(helpText);Output example:
-i, --input [Required] Input file path
-o, --output [Optional] Output file path
-v, --verbose [Optional] Enable verbose output
--count [Optional] Number of iterations
--level [Optional,Enum] Log level
--level Debug Info Warning Error
--features [Optional,Flags] Enabled features
--features Logging Caching Compression
Implement IFormatter for custom help text formatting:
public interface IFormatter
{
void Append(StringBuilder stringBuilder, string name, string attributes,
string? helpText, string? usage);
}// Use custom formatter
var customFormatter = new MyCustomFormatter();
string helpText = Parser.GetHelpText(options, customFormatter);// Default indent and blank spaces
Parser.DefaultIndent // 4 spaces for left indentation
Parser.DefaultBlank // 43 characters for option name column width| Method | Description |
|---|---|
Parse<T>(string arguments) |
Parse a command-line string |
Parse<T>(IEnumerable<string> arguments) |
Parse an array of arguments |
Parse<T>(string arguments, T value) |
Parse into an existing instance |
FormatCommandLine(object, CommandLineFormatMethod) |
Convert object to command-line string |
FormatCommandLineArgs(object, CommandLineFormatMethod) |
Convert object to string array |
GetHelpText(object, IFormatter?) |
Generate help text |
GetTypeInfo<T>() |
Get cached type metadata |
| Member | Description |
|---|---|
Parser.Default |
Default parser instance with default settings |
| Property | Description |
|---|---|
Result |
Parsed or NotParsed |
Value |
The parsed options object |
ErrorMessage |
Error details if parsing failed |
Type |
Type metadata information |
| Property | Description |
|---|---|
ShortName |
Single character option (e.g., 'v' for -v) |
LongName |
Full option name (e.g., "verbose" for --verbose) |
Required |
Whether the option must be provided |
HelpText |
Description shown in help output |
Index |
Positional argument index (>= 0 makes it positional, default: -1) |
MetaName |
Display name for positional arguments in help text |
Separator |
Custom separator character for array/list values (default: none) |
| Flag | Value | Description |
|---|---|---|
None |
0 | Default space-separated style |
Complete |
1 | Output all options including defaults |
Simplify |
2 | Only output non-default values |
EqualSignStyle |
4 | Use --option=value syntax |
// Good
[Option('o', "output", HelpText = "Output file path")]
public string OutputPath { get; set; }
// Avoid
[Option('x', "x")]
public string X { get; set; }public class Options
{
[Option("timeout", HelpText = "Request timeout in seconds")]
public int Timeout { get; set; } = 30; // Sensible default
[Option("retries", HelpText = "Number of retry attempts")]
public int Retries { get; set; } = 3;
}var result = Parser.Default.Parse<Options>(args);
if (result.Result != ParserResultType.Parsed)
{
Console.Error.WriteLine(result.ErrorMessage);
Console.WriteLine(Parser.GetHelpText(new Options()));
return 1; // Exit with error code
}
// Safe to use result.Value here[Option('i', "input", Required = true, HelpText = "Input file (required)")]
public string InputFile { get; set; }Here's a complete example demonstrating all features:
using MiniCommandLineParser;
public enum OutputFormat { Text, Json, Xml }
[Flags]
public enum ProcessingFlags
{
None = 0,
Validate = 1,
Transform = 2,
Compress = 4
}
public class Options
{
// Positional argument - the command to execute
[Option(Index = 0, MetaName = "COMMAND", HelpText = "Command to execute (process, convert, analyze)")]
public string Command { get; set; } = "";
// Positional argument - the input file
[Option(Index = 1, MetaName = "INPUT", HelpText = "Input file path")]
public string InputFile { get; set; } = "";
[Option('o', "output", HelpText = "Output file path")]
public string OutputFile { get; set; }
[Option('f', "format", HelpText = "Output format")]
public OutputFormat Format { get; set; } = OutputFormat.Text;
[Option("flags", HelpText = "Processing flags")]
public ProcessingFlags Flags { get; set; } = ProcessingFlags.Validate;
// Array with custom separator
[Option("tags", Separator = ';', HelpText = "Tags for the output (semicolon-separated)")]
public List<string> Tags { get; set; }
[Option("include", HelpText = "Additional files to include")]
public List<string> IncludeFiles { get; set; }
[Option('v', "verbose", HelpText = "Enable verbose logging")]
public bool Verbose { get; set; }
[Option("threads", HelpText = "Number of worker threads")]
public int ThreadCount { get; set; } = Environment.ProcessorCount;
}
class Program
{
static int Main(string[] args)
{
// Show help if requested
if (args.Length == 0 || args.Contains("--help") || args.Contains("-h"))
{
Console.WriteLine("Usage: myapp <command> <input> [options]");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(Parser.GetHelpText(new Options()));
return 0;
}
// Parse arguments
var result = Parser.Default.Parse<Options>(args);
if (result.Result != ParserResultType.Parsed)
{
Console.Error.WriteLine($"Error: {result.ErrorMessage}");
return 1;
}
var options = result.Value;
// Display parsed options
if (options.Verbose)
{
Console.WriteLine("Parsed options:");
Console.WriteLine($" Command: {options.Command}");
Console.WriteLine($" Input: {options.InputFile}");
Console.WriteLine($" Output: {options.OutputFile ?? "(stdout)"}");
Console.WriteLine($" Format: {options.Format}");
Console.WriteLine($" Flags: {options.Flags}");
Console.WriteLine($" Threads: {options.ThreadCount}");
if (options.Tags?.Count > 0)
Console.WriteLine($" Tags: {string.Join(", ", options.Tags)}");
if (options.IncludeFiles?.Count > 0)
Console.WriteLine($" Includes: {string.Join(", ", options.IncludeFiles)}");
}
// Format back to command line for logging
Console.WriteLine($"Effective command: {Parser.FormatCommandLine(options, CommandLineFormatMethod.Simplify | CommandLineFormatMethod.EqualSignStyle)}");
// Your application logic here...
return 0;
}
}Run examples:
# Using positional arguments
myapp process data.txt -o result.json -f Json -v
# Positional args with named options mixed
myapp --verbose process data.txt --format=Xml --threads=8
# With custom separator for tags
myapp convert input.csv --tags=important;reviewed;final -o output.json
# With flags and includes
myapp analyze report.txt --flags Validate Transform --include extra1.txt extra2.txt
# Using equals syntax throughout
myapp process data.txt -o=result.json --format=Json --threads=4 --tags=dev;testThis project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Author: bodong
- GitHub: https://github.com/bodong1987/MiniCommandLineParser
Made with β€οΈ for the .NET community