Simple yet powerfull library to made parsing CLI arguments easy. Library also allow to print usage help and application version “out of box”.
For example of usage check Examples directory.
Cli.Configure(configure => configure
.SetDialect(Dialect.Gnu)
.SetName("QuickStart")
.SetDescription("This is NFlags")
.ConfigureVersion(vc => vc.Enable())
)
.Root(rc => rc
.RegisterFlag("flag1", "f", "Flag description", false)
.RegisterOption("option", "o", "Option description", "optionDefaultValue")
.RegisterParameter("param", "Param description", "ParamDefaultValue")
.RegisterCommand("subcommand", "Subcommand Description", sc => sc
.SetExecute((commandArgs, output) => output.WriteLine("This is subcommand: " + commandArgs.GetParameter<string>("SubParameter")))
.RegisterParameter("SubParameter", "SubParameter description", "SubParameterValue")
)
.RegisterParamSeries("paramSeries", "paramSeriesDescription")
.SetExecute((commandArgs, output) => output.WriteLine("This is root command: " + commandArgs.GetParameter<string>("param")))
).
Run(args);
Run application and enjoy:
$> dotnet NFlags.QuickStart.dll
This is root command: ParamDefaultValue%
$> dotnet NFlags.QuickStart.dll xxx
This is root command: xxx
$> dotnet NFlags.QuickStart.dll --help
Usage:
QuickStart [COMMAND] [OPTIONS]... [PARAMETERS]...
This is NFlags
Commands:
command Sub command Description
Parameters:
<param> Param description (Default: 'ParamDefaultValue')
<paramSeries...> paramSeriesDescription
Options:
--flag1, -f Flag description
--option <option>, -o <option> Option description (Default: 'optionDefaultValue')
--help, -h Prints this help
--version, -v Prints application version
$> dotnet NFlags.QuickStart.dll subcommand
This is subcommand: SubParameterValue
$> dotnet NFlags.QuickStart.dll subcommand yyy
This is subcommand: yyy
$> dotnet NFlags.QuickStart.dll command --help
Usage:
QuickStart command [OPTIONS]... [PARAMETERS]...
This is NFlags
Parameters:
<Parameter> Sub parameter description (Default: 'SubParameterValue')
Options:
--help, -h Prints this help
--version, -v Prints application version
$>
Name set with following code, will be printed in help.
By default AppDomain.CurrentDomain.FriendlyName
is used.
Cli.Configure(configurator => configurator.SetName("Custom Name"));
Description set with following code, will be printed in help.
Cli.Configure(configurator => configurator.SetDescription("App description"));
Output adapter set by this method is used to produce output. Default Console
is used.
Cli.Configure(configurator => configurator.SetOutput(Output.Console));
Environment adapter set by this method is used to read environment variables. Default System
is used.
Additional Prefixed adapter is provided within library and can be used when all variabels should by started with same prefix to not define prefixed name in every usage.
Cli.Configure(configurator => configurator.SetEnvironment(Environment.System);
Configuration adapter set by this method is used to read values from configuration. If not set, ConfigPath
is not used when reading walue of argument.
Cli.Configure(configurator => configurator.SetConfiguration(...);
There are two types of configuration providers IConfig
and IGenericConfig
.
IGenericConfig
require to return value in expected type (for argument using it), where When using IConfig
value returned by Get
method is parsed using Converters. See Converters section.
When both IConfig
and IGenericConfig
are provided, generic one takes precedence.
Dialect defines how flags and options are prefixed and how option value follows option.
Cli.Configure(configurator => configurator.SetDialect(Dialect.Gnu));
NFlags always has at least one root command
Flag is an prefixed argument with boolean value. It can be turned on or off. Flag abbreviation can be also set. There is also default value which will be negated when flag will be passed as argument.
Cli.Configure(c => {}).Root(configurator => configurator.RegisterFlag("flag", "f", "Flag description", true));
Cli.Configure(c => {}).Root(configurator => configurator.RegisterFlag("flag", "Flag description", false));
Alternative way of setting flag is to use flagBuilder.
Cli
.Configure(c => {})
.Root(configurator => configurator
.RegisterFlag(flagBuilder => flagBuilder
.Name("flag")
.Abr("f")
.Description("Flag description")
.EnvironmentVariable("NFLAGS_FLAG") //.LazyEnvironmentVariable("NFLAGS_FLAG")
.ConfigPath("app.settings.flag") //.LazyConfigPath("app.settings.flag")
.Persistent()
.DefaultValue(true)
)
);
When registering flag, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
Same goes to setting config path, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
If flag default value is set to True
, falg will be set to False
when passed from CLI.
Option is an prefixed argument with value. Option abbreviation can be also set. Values are converted to type T. CLR types, classes with implicit operator from string and classes with string argument constructor are supported by default. For other types see converters section.
Cli.Configure(c => {}).Root(configurator => configurator.RegisterOption("option", "o", "option description", "defaultOptionValue"));
Cli.Configure(c => {}).Root(configurator => configurator.RegisterOption("option", "option description", "defaultOptionValue"));
Alternative way of setting option is to use optionBuilder.
Cli
.Configure(c => {})
.Root(configurator => configurator
.RegisterOption<double>(optionBuilder => optionBuilder
.Name("option")
.Abr("o")
.Description("option description")
.EnvironmentVariable("NFLAGS_OPTION") // .LazyEnvironmentVariable("NFLAGS_OPTION")
.ConfigPath("app.settings.option") // .LazyConfigPath("app.settings.flag")
.Persistent()
.DefaultValue(1.1)
.Converter(new ValueConverter())
)
);
When registering option, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
Same goes to setting config path, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
Parameter is an unprefixed value argument. Parameters are read by registration order. Values are converted to type T. CLR types, classes with implicit operator from string and classes with string argument constructor are supported by default. For other types see converters section.
Cli.Configure(c => {}).Root(configurator => configurator.RegisterParameter("param", "Param description", "paramDefaultValue"));
Alternative way of setting parameter is to use parameterBuilder.
Cli
.Configure(c => {})
.Root(configurator => configurator
.RegisterParameter<double>(parameterBuilder => parameterBuilder
.Name("parameter")
.Description("parameter description")
.EnvironmentVariable("NFLAGS_PARAMETER") // .LazyEnvironmentVariable("NFLAGS_PARAMETER")
.ConfigPath("app.settings.parameter") // .LazyConfigPath("app.settings.parameter")
.DefaultValue(1.2)
.Converter(new ValueConverter())
)
);
When registering parameter, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
Same goes to setting config path, builder contains either EnvironmentVariable
and LazyEnvironmentVariable
methods. If LazyEnvironmentVariable
is used, the variable will be resolved using provider when accessing command arg value, otherwise during initialisation.
Parameter series is a collection of parameters after last named parameter. Parameter series can be used to parse unknown count of parameters to process i.e. strings to concat. Values are converted to type T. CLR types, classes with implicit operator from string and classes with string argument constructor are supported by default. For other types see converters section.
Cli.Configure(c => {}).Root(configurator => configurator.RegisterParamSeries<int>("paramSeries", "Param series description"));
There is also non-generic method where argument type is string
Cli.Configure(c => {}).Root(configurator => configurator.RegisterParamSeries("paramSeries", "Param series description"));
Both flags and options can attached to groups and printed in separate section in help.
Cli.Configure(c => {}).Root(c => c.RegisterFlag(b => b.Name("flag").Group("group"));
Cli.Configure(c => {}).Root(c => c.RegisterOption<string>(b => b.Name("option").Group("group"));
The following code:
Cli
.Configure(c => c
.SetDialect(Dialect.Gnu)
)
.Root(c => c
.RegisterParameter<string>(b => b.Name("param1"))
.RegisterFlag(b => b.Name("flag1"))
.RegisterFlag(b => b.Name("group1-flag1").Group("group1"))
.RegisterFlag(b => b.Name("group2-flag1").Group("group2"))
.RegisterFlag(b => b.Name("group1-flag2").Group("group1"))
.RegisterFlag(b => b.Name("flag2"))
.RegisterOption<string>(b => b.Name("option1"))
.RegisterOption<string>(b => b.Name("group1-option1").Group("group1"))
.RegisterOption<string>(b => b.Name("group2-option1").Group("group2"))
.RegisterOption<string>(b => b.Name("group1-option2").Group("group1"))
.RegisterOption<string>(b => b.Name("option2"))
.PrintHelpOnExecute()
)
.Run(args);
will print following help test
$> dotnet NFlags.Groups.dll
Usage:
NFlags.Groups [OPTIONS]... [PARAMETERS]...
Parameters:
<param1>
Options:
--flag1
--flag2
--option1 <option1>
--option2 <option2>
--help, -h Prints this help
group1:
--group1-flag1
--group1-flag2
--group1-option1 <group1-option1>
--group1-option2 <group1-option2>
group2:
--group2-flag1
--group2-option1 <group2-option1>
To attach code to execution by command, simply call SetExecution
method of command configurator and pass Action<CommandArgs, Action<string>>
callback.
First argument of action contains all registered Flags, Options and Parameters with default or given values. The second one is callback to print output from command.
Cli.Configure(c => {}).Root(configurator => configurator.SetExecute((commandArgs, output) => output.WriteLine("This is command output: " + commandArgs.GetParameter<string>("param")));
If execute is of type Func<CommandArgs, IOutput, int>
result will be returned by Bootstrap.Run
to be used as exit code.
To attach subcommand, call RegisterCommand
method of command configurator.
The third parameter is a configurator for the sub command and can be used in the same Way as the one for root command..
Cli.Configure(c => {}).
Root(configurator => configurator.
RegisterCommand("subcommand", "Subcommand Description", sc => sc.
SetExecute((commandArgs, output) => output.WriteLine("This is subcommand: " + commandArgs.GetParameter<string>("SubParameter"))).
RegisterParam("SubParameter", "SubParameter description", "SubParameterValue")
)
);
Default command can be defined, when registering command with RegisterDefaultCommand
. If default command is registered, it will be executed, when application is called to execute parent command.
Cli.Configure(c => {}).
Root(configurator => configurator.
RegisterDefaultCommand("default", "Default command Description", sc => sc.
SetExecute((commandArgs, output) => output.WriteLine("This is default command"))
)
);
To parse arguments and execute requested command call:
Cli.Configure(c => {}).Root(configurator => {}).Run(args);
Dialect defines how flags and options are prefixed and how option value follows option. By default 2 dialect are supported: Gnu, Win
Dialect can be set (default is Win) using:
Cli.Configure(configurator => configurator.SetDialect(Dialect.Gnu));
Dialects can be easily extended. To create new dialect simply create new class inherited from Dialect class.
public class CustomDialect : Dialect
{
public override string Prefix => "x";
public override string AbrPrefix => "a";
public override OptionSeparator OptionSeparator => OptionSeparator.ArgSeparator;
}
and configure NFlags using it
Cli.Configure(configurator => configurator.SetDialect(new CustomDialect()));
Win dialect, follows MS Windows standards for defining console app arguments:
Gnu dialect, follows Gnu Linux standards for defining console app arguments:
Help generation is supported “out of box” and it follows dialect rules. To print help use:
Console.WriteLine(Cli.Configure(configurator => {}).PrintHelp());
Example help for Win dialect:
Usage:
NFlags.Win [COMMAND] [OPTIONS]... [PARAMETERS]...
Application description
Commands:
show Show somethig
list List something
Parameters:
<param1> Parameter 1 description (Default: '.')
Options:
/verbose, /v Verbose description
/clear Clear description
/option1=<option1>, /o1=<option1> Option 1 description (Default: 'default')
/option2=<option2> Option 2 description (Default: 'default2')
/help, /h Prints this help
and for Gnu dialect:
Usage:
NFlags.Gnu [COMMAND] [OPTIONS]... [PARAMETERS]...
Application description
Commands:
show Show somethig
list List something
Parameters:
<param1> Parameter 1 description (Default: '.')
Options:
--verbose, -v Verbose description
--clear Clear description
--option1 <option1>, -o1 <option1> Option 1 description (Default: 'default')
--option2 <option2> Option 2 description (Default: 'default2')
--help, -h Prints this help
Command can be also configured to print help on execute. This is useful when creating command who aggregates set of sub commands. The following code:
Cli
.Configure(c => c.SetDialect(Dialect.Gnu))
.Root(c => c.PrintHelpOnExecute())
.Run(args);
will print following output:
$> dotnet NFlags.Gnu.dll
Usage:
NFlags.Gnu [OPTIONS]... [PARAMETERS]...
Parameters:
<parameter> parameter description
<parameterSeries...> parameter series description
Options:
--flag, -f flag description
--option <option>, -o <option> option description
--help, -h Prints this help
By default help option use help
(with h
abbreviation) to define print help option. This can be customized in help configuration
Cli.Configure(c => c.ConfigureHelp(hc => hc.SetOptionFlag("xhelp").SetOptionAbr("x")))
$> dotnet NFlags.CustomHelpFlags.dll -x
Usage:
NFlags.CustomHelpFlags [OPTIONS]...
Options:
--xhelp, -x Prints this help
Default help text for help option is Prints this help
. This can be customized in help configuration
Cli.Configure(c => c.ConfigureHelp(hc => hc.SetOptionDescription("custom description")))
$> dotnet NFlags.CustomHelpDesc.dll
Usage:
NFlags.CustomHelpFlags [OPTIONS]...
Options:
--help, -h custom description
HelpPrinter is used to generate text output from NFlags configuration represented by CommandConfig
. The default implementation can be replaced trough configuration to customize help printing.
Cli.Configure(c => c.ConfigureHelp(hc => hc.SetPrinter(new CustomHelpPrinter())))
CustomHelpPrinter
must implement IHelpPrinter
interface.
Printing application Version is supported “out of box”. By default versions are disabled.
When enabled version option allows to print application version using special argument.
Cli.Configure(configurator => configurator.ConfigureVersion(vc => vc.Enable()));
By default version option use version
(with v
abbreviation) to define print version option. This can be customized in version configuration
Cli.Configure(c => c.ConfigureVersion(vc => vc.Enable().SetOptionFlag("xversion").SetOptionAbr("x")))
$> dotnet NFlags.CustomVersionFlags.dll -x
Usage:
NFlags.CustomVersionFlags [OPTIONS]...
Options:
--help, -h Prints this help
--xversion, -x Prints application version
Default help text for version option is Prints application version
. This can be customized in version configuration
Cli.Configure(c => c.ConfigureVersion(vc => vc.Enable().SetOptionDescription("custom version description")))
$> dotnet NFlags.CustomVersionFlags.dll
Usage:
NFlags.CustomVersionFlags [OPTIONS]...
Options:
--help, -h Prints this help
--version, -v custom version description
Generics are an alternative method of registering commands arguments. Generic cannot be mixed with basic methods to configure command. To use generic way of registering commands with arguments custom type with fields or set properties with dedicated attributes is required.
public class RootCommandArguments
{
[Option("option", "o", "option description", 3)]
public int Option;
[Flag("flag", "f", "flag description", true)]
public bool Flag;
[Parameter("parameter", "parameter description", 1.1)]
public double Parameter;
[ParameterSeries("parameterSeries", "parameter series description")]
public int[] ParameterSeries;
}
public class Command2Arguments
{
[Option("option2", "o2", "option description", 3)]
public int Option { get; set; }
[Flag("flag2", "f2", "flag description", true)]
public bool Flag { get; set; }
[Parameter("parameter2", "parameter description", 1.1)]
public double Parameter { get; set; }
[ParameterSeries("parameterSeries2", "parameter series description")]
public int[] ParameterSeries { get; set; }
}
Then command can be configured using generic versions of methods:
Cli
.Configure(c => c
.SetDialect(Dialect.Gnu)
)
.Root<RootCommandArguments>(c => c
.PrintHelpOnExecute()
.RegisterCommand<RootCommandArguments>("command1", "this is command 1", configurator => configurator
.PrintHelpOnExecute()
)
)
See NFlags.Generics
example.
Generics also supports lazy environment and configuration binding. When property is of type Lazy<>
the value will be
resolved when accessed by Value
property of Lazy<>
type.
public class RootCommandArguments
{
[Option(Name = "option", ConfigPath = "config.option", EnvironmentVariable = "NFLAGS_OPTION_TEST_ENV", DefaultValue = 1)]
public Lazy<int> Option;
[Flag(Name = "flag", ConfigPath = "config.flag", EnvironmentVariable = "NFLAGS_FLAG_TEST_ENV", DefaultValue = true)]
public Lazy<bool> Flag;
[Flag(Name = "parameter", ConfigPath = "config.parameter", EnvironmentVariable = "NFLAGS_FLAG_PARAMETER_ENV", DefaultValue = 1.1)]
public Lazy<double> Parameter;
}
When option is registered with array type NFlags allows to pass argument multiple times from CLI and aggregate it into array.
Running the following code:
Cli.Configure(c => c
.SetDescription("Application description")
.SetDialect(Dialect.Gnu)
.SetOutput(Output.Console))
.Root(c => c
.RegisterOption<string[]>(b => b.Name("array-option").DefaultValue(new string[0]))
.SetExecute((commandArgs, output) =>
{
foreach (var option in commandArgs.GetOption<string[]>("carray-option"))
output.WriteLine(option);
})
)
.Run(args);
Will result:
$> dotnet NFlags.Array.dll --array-option a --array-option b --array-option c
a
b
c
Converters are used to convert argument (option or parameter) value to expected type.
Converter must implement interface NFlags.TypeConverters.IArgumentConverter
interface.
public class UserConverter : IArgumentConverter
{
public bool CanConvert(Type type)
{
return typeof(User) == type;
}
public object Convert(Type type, string value)
{
var strings = value.Split(";");
if (strings.Length != 3)
throw new ArgumentValueException(type, value);
return new User
{
UserName = strings[0],
Name = strings[1],
Password = strings[2]
};
}
}
When registering parameter/option, existence of converter is checked, otherwise NFlags.TypeConverters.MissingConverterException
is thrown.
When parsing arguments, first matching converter is used (in registration order).
Cli.Configure(configurator => configurator.RegisterConverter(new UserConverter()));
Converter can be also set directly for argument trough builder method. If passed the converter from argument definition is used ,intstead of global registerd converters.
Cli
.Configure(c => {})
.Root(configurator => configurator
.RegisterOption<double>(b => b
.Name("option")
.Converter(new ValueConverter())
)
.RegisterParameter<double>(b => b
.Name("parameter")
.Converter(new ValueConverter())
)
.RegisterParameterSeries<double>(b => b
.Name("parameterSeries")
.Converter(new ValueConverter())
)
);
Default policy of exception handling in NFlags is to not throw exception when user use CLI incorrectly. If Exception is thrown during parsing arguments, exception message and help is printed and exit code 255 is returned. For the following application:
public static int Main(string[] args)
{
return Cli
.Configure(c => { })
.Root(c => c
.RegisterParam("param", "param description", 1)
.SetExecute((commandArgs, output) => { }))
.Run(args);
}
Execution of:
$> dotnet Program.dll asd
Will print:
Cannot convert value 'as' to type 'System.Int32'
Usage:
NFlags.Empty [OPTIONS]... [PARAMETERS]...
Parameters:
<param> param description
Options:
/help, /h Prints this help
Process finished with exit code 255.
Exception handling can be disabled using configuration:
Cli.Configure(c => c.DisableExceptionHandling());
If exception handling is disabled NFlags can throw NFlags.TooManyParametersException
or NFlags.TypeConverters.ArgumentValueException
.