Automatic command-line verb and argument parsing using smart reflection.
Inspired by the ASP.NET MVC Controller-Actions, finally – a kick-ass command-line parser. The last one you will ever need!
Update: Part 2 of the CLAP introduction is here
Get It
- Source code and binaries at github: https://github.com/adrianaisemberg/CLAP
- NuGet: Install-Package CLAP
Usage Samples
Verbs
Consider the following method:
{
[Verb]
public static void Print(string prefix, string name, int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine("{0} {1}", prefix, name);
}
}
}
The Print method is marked with the [Verb] attribute which defines method as valid entry points for the application.
Each of the method’s arguments are automatically mapped to command-line input arguments, regardless of their order.
Notice that all names, both for verbs and parameter names are case-insensitive.
Let’s change our Main method to use the parser:
{
static void Main(string[] args)
{
Parser<MyApp>.Run(args);
}
}
After successfully compiling, we can now run our application, providing a verb and arguments:
Which prints:
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
More Verbs for the same program
A class may contain more than one verb.
In the following example, “print” and “login” are two different entry-points for the application, each with a different set of arguments:
{
[Verb]
public static void Print(string prefix, string name, int count)
{
}
[Verb]
public static void Login(string userName, string password)
{
}
}
A Default Verb
One of the verbs, or even the single one if no more than one are available, can be defined as the default verb using the [DefaultVerb] attribute above the class.
In the following example, “print” is the default verb, so if the user doesn’t provide a verb in the command-line, “print” will be called:
class MyApp
{
[Verb]
public static void Print(string prefix, string name, int count)
{
}
[Verb]
public static void Login(string userName, string password)
{
}
}
In that case, we can call:
Aliases
A verb can have more than one name. The first name is the method’s name but additional names can be defined using the Aliases property.
In the following example, “p” is an additional name for the “print” verb and both “l” and “log” are additional names for the “login” verb:
class MyApp
{
[Verb(Aliases = "p")]
public static void Print(string prefix, string name, int count)
{
}
[Verb(Aliases = "l,log")]
public static void Login(string userName, string password)
{
}
}
Parameters
Parameters are automatically detected according to their names. Additional options can be defined for parameters using more attributes.
Aliases
As a verb, parameters can also have additional names when marked with the [Parameter] attribute.
In the following example, both “name” and “n” are additional names for the “userName” parameter and both “pass” and “p” are for the “password” parameter:
{
[Verb]
public static void Login(
[Parameter(Aliases = "name,n")]
string userName,
[Parameter(Aliases = "pass,p")]
string password)
{
}
}
Default Values
A parameter can be omitted from the input. If a parameter is omitted, it’s value will have the type’s default (null for reference types, zero for value types).
A default value can be defined for a parameter to override the type’s default.
In the following example, “Hello” is the default for the prefix parameter and 5 for count:
{
[Verb]
public static void Print(
[Parameter(Default = "Hello")]
string prefix,
string name,
[Parameter(Default = 5)]
int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine("{0} {1}", prefix, name);
}
}
}
Now we can run:
And get this result:
Hello World
Hello World
Hello World
Hello World
Switches
Boolean types are treated as switches.
A switch is an argument that doesn’t need a value. If the argument exists – the value is true, otherwise – it is false.
In the following example, the “upper” parameter is a boolean, and therefore – a switch. In-addition, it has an additional, shorter name: “u”:
{
[Verb]
public static void Print(
[Parameter(Default = "Hello")]
string prefix,
string name,
[Parameter(Default = 5)]
int count,
[Parameter(Aliases = "u")]
bool upper)
{
if (upper)
{
prefix = prefix.ToUpper();
name = name.ToUpper();
}
for (int i = 0; i < count; i++)
{
Console.WriteLine("{0} {1}", prefix, name);
}
}
}
Now we can run:
HELLO WORLD
HELLO WORLD
HELLO WORLD
HELLO WORLD
HELLO WORLD
Parameter Types
The supported parameter types are:
- Numeric Types (int, float, …)
- Boolean – can be used as switches but also with “true”/”false” input
- String
- Any Enum (input is case-sensitive)
- Arrays of all the above
Arrays
Arrays are called as comma-separated values. For example:
{
[Verb]
public static void Hello(string[] names)
{
foreach (var name in names)
{
Console.WriteLine("Hello {0}", name);
}
}
}
Hello Adrian
Hello Yoav
Hello Shlomo
Required Parameters
A parameter can be marked as required. A missing argument will throw a MissingRequiredArgumentException.
In the following example, the “name” parameter is required:
{
[Verb]
public static void Print(
[Parameter(Default = "Hello")]
string prefix,
[Parameter(Required = true)]
string name)
{
}
}
Trying to run the program without providing a value for “name” will throw an exception.
In this example, the exception is not caught and therefore, the application crashes and the stack-trace is printed to the console:
Unhandled Exception: System.ArgumentException: Missing argument for 'name'
at …
Validation
Validation attributes can be specified for parameters.
Current available attributes are:
- MoreThan
- MoreOrEqualTo
- LessThan
- LessOrEqualTo
- RegexMatches
Providing a value that doesn’t pass validation will throw a ValidationException.
In the following example, “count” is validated to be more than 3:
{
[Verb]
public static void Print(
string prefix,
string name,
[MoreThan(3)]
int count)
{
}
}
Additional validation attributes can be defined for the same parameter. Validation passes only if all validators pass (AND).
In the following example, “count” is validated to be more than 3 and less-or-equal to 20:
{
[Verb]
public static void Print(
string prefix,
string name,
[MoreThan(3)]
[LessOrEqualTo(20)]
int count)
{
}
}
Exceptions
The following is a list of all the exceptions that might be thrown when failing to parse the command-line.
Notice that all exception derive from the abstract CommandLineException.
MissingDefaultVerbException
- The input does not contain a verb and no default verb was defined.
- Solution: Either input a verb or define one using the [DefaultVerb] attribute.
MissingVerbException
- The input contains a verb but no method matches it.
- Solution: Check that the method is marked with the [Verb] attribute or that the input verb is one of the verb’s aliases.
MissingRequiredArgumentException
- A required argument is missing.
- Solution: If the parameter is marked as [Parameter(Required = true)], check that an argument is actually passed for that parameter.
MissingArgumentValueException
- A value argument is missing from a parameter input, and the parameter is not a switch (the type is not a boolean).
- Solution: Make sure the argument value is passed in the form of: -name:value
MissingArgumentPrefixException
- A parameter does not have a prefix of either “-“ or “/”.
- Solution: Make sure to prefix all parameters with a valid prefix.
ValidationException
- An argument value does not pass validation.
- Solution: Make sure the value passes the defined validation.
TypeConvertionException
- An argument value cannot be converted to the parameter’s type, for example: A string input for an int type.
- Solution: Make sure the input value matches the parameter’s type.
Summary
This post introduced the Command-Line Auto Parser and provided examples for:
- Defining verbs
- Defining parameters
- Default verbs and additional names
- Default values
- Supported parameter types
- Using built-in validation
- Exceptions and how to handle them
In the next post, additional features are introduced:
- Global parameter handlers
- Custom validation
- Providing help to the user










Nice work! Looks really good.
Is CLAP available via nuget?
Cheers
Marvelous!
I’ll invest some time and make it available via nuget.
Pingback: The Morning Brew - Chris Alcock » The Morning Brew #869
Sweet library – I didn’t see any example of it producing an automatic “usage/help” message.
Is this something that already exists, or something for the future?
It exists, but still in development.
Next post will introduce it, along with additional features.
Smart! Does it work in PowerShell as well?
Yes, but as a regular exe.
You won’t get any Powershell specific features, such as auto-complete (by pressing tab).
Any chance we could see the source code? This looks like a great opportunity for learning. Nice job!
Regarding the source code:
After publishing the second part (which introduces additional features) I will publish the source code.
Very promising!
i will give it i try for sure
i’ve just noticed that your validation doesnt use the DataAnnotations namespace. Is there a reason for that?
These are my implementations of parameter validations.
They are not related to DataAnnotations.
Looks really nice!
Which versions of .NET will this run under. Most of my command line utilities are still required to run under 2.0 (or later) believe it or not.
It requires .NET 3.5
Very nice work!
When we can see the source code? I’m hungry, to learn from your ideas…
Soon
The second part is almost ready. After that – I will publish the code.
What license are you using?
License?
No license. It’s free.
Pingback: Found on the Web – 13 June 2011 | Phillpotts Dot Net
Pingback: C#: CLAP, a command-line auto parser | Jesus Was Rasta
Pingback: .NET i jiné ... : Odkazy z prohlíže?e - 24.6.2011
Hi Adrian.
I’m thinking about possibility to configure commands and parameter markers; now if I want to execute a command with some parameters I have to do something like this:
C:\>myapp.exe mycommand -myparam1:something -myparam2:somethingelse
But if my old application have a pattern like this:
C:\>myapp.exe –mycommand:something,somethingelse (ordered params with comma, semicolon or other separator)
or
C:\>myapp.exe /mycommand -myparam1:something -myparam2:somethingelse
I cannot replace my old command-line parser with CLAP at 1:1, but I need to write a wrapper for it ().
Please let me know if you like my idea to have a “pattern configuration” feature for CLAP.
Bests,
Nando
Feel free to clone the CLAP git repo and implement such one
Well, I will try, thank you!