See Also: OptionSet Members
A common requirement for command-line programs is option parsing. The command line consists of a sequence of arguments. OptionSet.Parse(IEnumerable<string>) parses this argument sequence, invoking actions of registered Mono.Options.Options when a sequence of arguments matching the Mono.Options.Options requirements are encountered. Mono.Options.Option's are registered with OptionSet.Add(Option) and its overloads.
OptionSet.Parse(IEnumerable<string>), returns a List<string> of all arguments which were not matched by a registered Mono.Options.Option.
Response files, as used by mcs and other compiler tools, may be supported by using Mono.Options.ResponseFileSource.
"Headers" -- string constants which allow grouping of options in the output of OptionSet.WriteOptionDescriptions(System.IO.TextWriter) -- may be provided "inline" with the actual options. The greet example below shows such usage.
Three conditions must be met for a sequence of arguments to be considered as a match for an Mono.Options.Option:
Simple names are matched when the argument consists solely of a flag followed by an option name.
Examples: -opt, --opt, and /opt are all simple names for the opt option.
Boolean names are matched when the argument consists of a flag, followed by an option name, followed by + or -. If a + follows the name, the option is enabled, otherwise the option is disabled.
Examples: -opt+ and -opt- enable and disble the opt option.
Assigned names are matched when the argument consists of a flag, followed by an option name, followed by a = or :, followed by an (optional) string.
Examples: -opt=A, --opt:B, and /opt=C all provide a value for the opt option.
Bundled names are matched only when simple, boolean, and assigned names are not matched, the flag is -, and the first character matches a registered option. Any number of (single-character) boolean options may be bundled in this fashion, and at most one OptionValueType.Optional or OptionValueType.Required option (which will use all remaining characters within the argument as a value).
Examples: -xvfinput-file could match the boolean Mono.Options.Options x and v and the value-accepting option f, which will get the value input-file. (Other interpretations are also possible, depending upon which options are registered.)
As a fallback, a default handler may be registered which will handle all arguments which are not handled by any of the above matching algorithms. The default handler is designated by the name <> (which may be an alias for another named Mono.Options.Option).
Default handlers are useful for argument runs -- sequences of arguments that are not related to each other but are instead related to a prior option on the command line, and it isn't desirable to use named options for these. For example, mdoc-assemble --format=ecma DirA DirB --format=man FileC would process DirA and DirB with --format=ecma in effect, and then process FileC with --format=man in effect.
See Option(string, string, int) for additional details regarding default option handlers.
There are three types of Mono.Options.Options that can be parsed:
Optional value options, which correspond to a Option.OptionValueType value of OptionValueType.Optional. Optional value options are not "greedy"; they will only get their value only from the current argument. If the value is not present, default(T) is provided to their corresponding Action`1.
Thus, -opt:value would pass value to the action registered for opt, while -opt value would pass default(T) to the action registered for opt and value would be an unparsed argument.
Required value options, which correspond to a Option.OptionValueType value of OptionValueType.Required. Required value options are "greedy" -- if the value is not found within the current argument, the following argument(s) will be used to supply the value(s). Once enough values have been parsed, their corresponding Action`1 is invoked.
Thus, both -opt:value and -opt value would pass value to the action registered for opt.
If no value can be found, an Mono.Options.OptionException is thrown from OptionSet.Parse(IEnumerable<string>).
Use of OptionSet is split up into two parts:
During the initialization phase, new Mono.Options.Option instances are created and associated with an action to perform when the Option requirements are met (e.g. when a required value has been encountered). This phase is not thread safe. All options added during this phase are considered to have been registered.
C# Example
OptionSet p = new OptionSet () {
{ "option-a", v => { /* action to perform */ } },
};There are three ways to add Mono.Options.Options to the Mono.Options.OptionSet:
During the parsing phase, an IEnumerable<string> is enumerated, looking for arguments which match a registered option, and invoking the corresponding action when an option and associated (optional) value is encountered. During this phase, the Mono.Options.OptionSet instance itself is thread safe, but full thread safety depends upon thread-safety of the registered actions. Any option-like strings for names that haven't been registered, e.g. --this-was-never-registered=false, and all arguments that are not used as option values are returned from OptionSet.Parse(IEnumerable<string>) or processed by the default handler <>, if registered.
C# Example
List<string> extra = p.Parse (new string[]{"-option-a"});Subclasses can override the following virtual methods to customize option parsing behavior:
The following greet example demonstrates some simple usage of Mono.Options.OptionSet.
C# Example
using System;
using System.Collections.Generic;
using Mono.Options;
class Test {
static int verbosity;
public static void Main (string[] args)
{
bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;
var p = new OptionSet () {
"Usage: greet [OPTIONS]+ message",
"Greet a list of individuals with an optional message.",
"If no message is specified, a generic greeting is used.",
"",
"Options:",
{ "n|name=", "the {NAME} of someone to greet.",
v => names.Add (v) },
{ "r|repeat=",
"the number of {TIMES} to repeat the greeting.\n" +
"this must be an integer.",
(int v) => repeat = v },
{ "v", "increase debug message verbosity",
v => { if (v != null) ++verbosity; } },
{ "h|help", "show this message and exit",
v => show_help = v != null },
};
List<string> extra;
try {
extra = p.Parse (args);
}
catch (OptionException e) {
Console.Write ("greet: ");
Console.WriteLine (e.Message);
Console.WriteLine ("Try `greet --help' for more information.");
return;
}
if (show_help) {
p.WriteOptionDescriptions (Console.Out);
return;
}
string message;
if (extra.Count > 0) {
message = string.Join (" ", extra.ToArray ());
Debug ("Using new message: {0}", message);
}
else {
message = "Hello {0}!";
Debug ("Using default message: {0}", message);
}
foreach (string name in names) {
for (int i = 0; i < repeat; ++i)
Console.WriteLine (message, name);
}
}
static void Debug (string format, params object[] args)
{
if (verbosity > 0) {
Console.Write ("# ");
Console.WriteLine (format, args);
}
}
}
The output (under the influence of different command-line arguments) is:
sh Example
$ mono greet.exe --help
Usage: greet [OPTIONS]+ message
Greet a list of individuals with an optional message.
If no message is specified, a generic greeting is used.
Options:
-n, --name=NAME the NAME of someone to greet.
-r, --repeat=TIMES the number of TIMES to repeat the greeting.
this must be an integer.
-v increase debug message verbosity
-h, --help show this message and exit
$ mono greet.exe -v- -n A -name=B --name=C /name D -nE
Hello A!
Hello B!
Hello C!
Hello D!
Hello E!
$ mono greet.exe -v -n E custom greeting for: {0}
# Using new message: custom greeting for: {0}
custom greeting for: E
$ mono greet.exe -r 3 -n A
Hello A!
Hello A!
Hello A!
$ mono greet.exe -r not-an-int
greet: Could not convert string `not-an-int' to type Int32 for option `-r'.
Try `greet --help' for more information.
Notice how the output produced by --help uses the descriptions provided during OptionSet initialization. Notice that the Mono.Options.Option requiring a value (n|name=) can use multiple different forms of invocation, including: -n value, -n=value, -name value, -name=value, --name value, --name=value, /name value, and /name=value.
Notice also that the boolean v option can take three separate forms: -v and -v+, which both enable the option, and -v-, which disables the option. (The second greet invocation uses -v-, which is why no debug messages are shown.)
Finally, note that the action can specify a type to use. If no type is provided, the action parameter will be a string. If a type is provided, then System.ComponentModel.TypeConverter will be used to convert a string to the specified type.
The following example shows how options and values can be bundled together.
C# Example
using System;
using System.Linq;
using System.Collections.Generic;
using Mono.Options;
class Test {
public static void Main (string[] args)
{
var show_help = false;
var macros = new Dictionary<string, string>();
bool create = false, extract = false, list = false;
string output = null, input = null;
string color = null;
var p = new OptionSet () {
"Usage: bundling [OPTIONS]+",
"Demo program to show the effects of bundling options and their values",
"",
"gcc-like options:",
{ "D:", "Predefine a macro with an (optional) value.",
(m, v) => {
if (m == null)
throw new OptionException ("Missing macro name for option -D.",
"-D");
macros.Add (m, v);
} },
{ "d={-->}{=>}", "Alternate macro syntax.",
(m, v) => macros.Add (m, v) },
{ "o=", "Specify the output file", v => output = v },
"",
"tar-like options:",
{ "f=", "The input file", v => input = v },
{ "x", "Extract the file", v => extract = v != null },
{ "c", "Create the file", v => create = v != null },
{ "t", "List the file", v => list = v != null },
"",
"ls-like optional values:",
{ "color:", "control whether and when color is used",
v => color = v },
"",
"other:",
{ "h|help", "show this message and exit",
v => show_help = v != null },
// default
{ "<>",
v => Console.WriteLine ("def handler: color={0}; arg={1}", color, v)},
};
try {
p.Parse (args);
}
catch (OptionException e) {
Console.Write ("bundling: ");
Console.WriteLine (e.Message);
Console.WriteLine ("Try `greet --help' for more information.");
return;
}
if (show_help) {
p.WriteOptionDescriptions (Console.Out);
return;
}
Console.WriteLine ("Macros:");
foreach (var m in (from k in macros.Keys orderby k select k)) {
Console.WriteLine ("\t{0}={1}", m, macros [m] ?? "<null>");
}
Console.WriteLine ("Options:");
Console.WriteLine ("\t Input File: {0}", input);
Console.WriteLine ("\tOuptut File: {0}", output);
Console.WriteLine ("\t Create: {0}", create);
Console.WriteLine ("\t Extract: {0}", extract);
Console.WriteLine ("\t List: {0}", list);
Console.WriteLine ("\t Color: {0}", color ?? "<null>");
}
}
The output (under the influence of different command-line arguments) is:
sh Example
$ mono bundling.exe --help
Usage: bundling [OPTIONS]+
Demo program to show the effects of bundling options and their values
gcc-like options:
-D[=VALUE1:VALUE2] Predefine a macro with an (optional) value.
-d=VALUE1-->VALUE2 Alternate macro syntax.
-o=VALUE Specify the output file
tar-like options:
-f=VALUE The input file
-x Extract the file
-c Create the file
-t List the file
ls-like optional values:
--color[=VALUE] control whether and when color is used
other:
-h, --help show this message and exit
$ mono bundling.exe -D
bundling: Missing macro name for option -D.
Try `greet --help' for more information.
$ mono bundling.exe -DA -DB=C "-dD-->E" "-dF=>G" -d "H=>I" -cf input --color -ooutput
Macros:
A=<null>
B=C
D=E
F=G
H=I
Options:
Input File: input
Ouptut File: output
Create: True
Extract: False
List: False
Color: <null>
$ mono bundling.exe -cfv input
def handler: color=; arg=input
Macros:
Options:
Input File: v
Ouptut File:
Create: True
Extract: False
List: False
Color: <null>
$ mono bundling.exe -xctf input
Macros:
Options:
Input File: input
Ouptut File:
Create: True
Extract: True
List: True
Color: <null>
$ mono bundling.exe --color=auto -o output -finput
Macros:
Options:
Input File: input
Ouptut File: output
Create: False
Extract: False
List: False
Color: auto
$ mono bundling.exe --color=on A B --color=off C D
def handler: color=on; arg=A
def handler: color=on; arg=B
def handler: color=off; arg=C
def handler: color=off; arg=D
Macros:
Options:
Input File:
Ouptut File:
Create: False
Extract: False
List: False
Color: off
The following example shows a custom OptionSet subclass with the following additional functionality:
C# Example
// Case-Insensitive and Concatenating OptionSet
using System;
using System.Collections.Generic;
using Mono.Options;
class DemoOptionSet : OptionSet {
protected override void InsertItem (int index, Option item)
{
if (item.Prototype.ToLower () != item.Prototype)
throw new ArgumentException ("prototypes must be lower-case!");
base.InsertItem (index, item);
}
protected override OptionContext CreateOptionContext ()
{
return new OptionContext (this);
}
protected override bool Parse (string option, OptionContext c)
{
string f, n, s, v;
bool haveParts = GetOptionParts (option, out f, out n, out s, out v);
Option nextOption = null;
string newOption = option;
if (haveParts) {
string nl = n.ToLower ();
nextOption = Contains (nl) ? this [nl] : null;
newOption = f + n.ToLower () + (v != null ? s + v : "");
}
if (c.Option != null) {
// Prevent --a --b
if (c.Option != null && haveParts) {
if (nextOption == null) {
// ignore
}
else
throw new OptionException (
string.Format ("Found option `{0}' as value for option `{1}'.",
option, c.OptionName), c.OptionName);
}
// have a option w/ required value; try to concat values.
if (AppendValue (option, c)) {
if (!option.EndsWith ("\\") &&
c.Option.MaxValueCount == c.OptionValues.Count) {
c.Option.Invoke (c);
}
return true;
}
else
base.Parse (newOption, c);
}
if (!haveParts || v == null) {
// Not an option; let base handle as a non-option argument.
return base.Parse (newOption, c);
}
if (nextOption.OptionValueType != OptionValueType.None &&
v.EndsWith ("\\")) {
c.Option = nextOption;
c.OptionValues.Add (v);
c.OptionName = f + n;
return true;
}
return base.Parse (newOption, c);
}
private bool AppendValue (string value, OptionContext c)
{
bool added = false;
string[] seps = c.Option.GetValueSeparators ();
foreach (var o in seps.Length != 0
? value.Split (seps, StringSplitOptions.None)
: new string[]{value}) {
int idx = c.OptionValues.Count-1;
if (idx == -1 || !c.OptionValues [idx].EndsWith ("\\")) {
c.OptionValues.Add (o);
added = true;
}
else {
c.OptionValues [idx] += value;
added = true;
}
}
return added;
}
}
class Demo {
public static void Main (string[] args)
{
List<string> names = new List<string> ();
Dictionary<string,string> map = new Dictionary<string,string> ();
int repeat = 1;
OptionSet p = new DemoOptionSet () {
{ "n|name=", v => names.Add (v) },
{ "r|repeat:", (int v) => repeat = v },
{ "m|map=", (k,v) => map.Add (k, v) },
};
List<string> extra;
try {
extra = p.Parse (args);
}
catch (OptionException e) {
Console.Write ("subclass: ");
Console.WriteLine (e.Message);
return;
}
string message;
if (extra.Count > 0) {
message = string.Join (" ", extra.ToArray ());
}
else {
message = "Hello {0}!";
}
foreach (string name in names) {
for (int i = 0; i < repeat; ++i)
Console.WriteLine (message, name);
}
List<string> keys = new List<string>(map.Keys);
keys.Sort ();
foreach (string key in keys) {
Console.WriteLine ("Key: {0}={1}", key, map [key]);
}
}
}
The output (under the influence of different command-line arguments) is:
sh Example
$ mono subclass.exe -n A -Name=B --NAME=C /nAMe D Hello A! Hello B! Hello C! Hello D! $ mono subclass.exe --Repeat -name A $ mono subclass.exe -Name --Repeat 3 subclass: Found option `--Repeat' as value for option `-name'. $ mono subclass.exe --Map a b -mAp c=d /maP=e=f Key: a=b Key: c=d Key: e=f $ mono subclass.exe --map 'a\' 'b\' c 'd\' 'e\' f Key: a\b\c=d\e\f
Notice: