Skip to content
Simon Cahill edited this page Feb 28, 2023 · 3 revisions

Using getopt.net to parse application arguments

Parsing command-line arguments is a critical portion of most applications.
From overriding default values to completely modifiying the application's behaviour, command-line arguments are a powerful tool.

Support for GNU Extensions

getopt.net supports both the POSIX-like implementation and also the GNU-extensions for getopt.

optarg in short option

One of the many GNU extensions to getopt, is allowing an option argument to be added alongside a short opt.

This means that instead of requiring a space between for example -c someval, it is completely legal to also write -csomevalx.

Example

var getopt = new GetOpt {
    ShortOpts = "c:",
    AppArgs = new[] { "-c" "someval", "-csomevalx" }
};

var optChar = 0;

while ((optChar = getopt.GetNextOpt(out var optArg)) != -1) {
    Console.WriteLine($"Got option '{ (char)optChar }' with option \"{ optArg }\"")
}

// Output:
// => Got option 'c' with option "someval"
// => Got option 'c' with option "somvalx"

Almost POSIX-correct behaviour

Stopping option parsing

If ShortOpts is prefixed with a + or the environment variable POSIXLY_CORRECT is set, getopt.net will stop parsing options as soon as the first non-option argument is encountered.

Example:

var getopt = new GetOpt {
    ShortOpts = "+ab:cd:ef",
    AppArgs = new[] { "-b", "somevalue" }
};

// as soon as "somevalue" is encountered, getopt.net will cease parsing options
// instead, GetNextOpt will return (char)1 and outOptArg will be set to the encountered values

var optChar;
while ((getopt.GetNextOpt(out var optArg)) != -1) {
    if (optChar != 1) {
        Console.WriteLine($"Found argument '{ optChar }' with optArg { (optArg ?? "\"no argument\"") }");
    } else {
        Console.WriteLine($"Option parsing has stopped. Got argument: { optArg }");
    }
}

// Output:
// => Found argument 'b' with OptArg "no argument"
// => Option parsing has stopped. Got argument: somvalue

Treating all non-opt values as option 1

If ShortOpts is prefixed with a '-', then each non-option value which isn't the argument of an option is treated as though it were the argument for an option with the value of (char)1.

This is useful for parsing filenames for example.

Example:

var getopt = new GetOpt {
    ShortOpts = "hvc:", // only the 'c' option requires an argument
    AppArgs = new[] { "-cvalue", "filename.txt" "-hv", "filename2.txt" }
};

var optChar = 0;
while ((optChar = getopt.GetNextOpt(out var optArg)) != -1) {
    switch (optChar) {
        case 1:
            Console.WriteLine($"Got non-option value \"{ optArg }\"");
            break;
        default:
            Console.WriteLine($"Got option '{ (char)optChar }' with argument \"{ (optArg ?? "no optarg") }\"");
            break;
    }
}

// Output:
// => Got option 'c' with argument "value"
// => Got non-option value "filename.txt"
// => Got option 'h' with argument "no optarg"
// => Got option 'v' with argument "no optarg"
// => Got non-option value "filename2.txt"

Short option as long option

As of v0.3.x getopt.net does not support treating short options as long options.

In getopt, this is accomplished by suffing the short opt with a Semicolon.

If demand is high enough or someone is willing to implement this functionality, then it will be merged into the master branch.

Example:

var getopt = new GetOpt {
    ShortOpts = "a:b;cfg", // inserting "b;" results in undefined behaviour!
};

Short option with optional argument

As of v0.3.x getopt.net does not support optional arguments for short options.
This is likely to change in future!

In getopt, adding optional arguments to short opts is done by suffxing each legal argument with a double-colon.

Example:

var getopt = new GetOpt {
    ShortOpts = "ab:c::", // inserting "c::" results in indefined behaviour!
};

Short option with requred argument

getopt.net supports short options requiring arguments.
The behaviour of getopt.net will depend on the options you have set!

Example with IgnoreMissingArgument = true:

var getopt = new GetOpt {
    ShortOpts = "ab:cd:",
    IgnoreMissingArgument = true // GetNextOpt will return MissingOptChar if an argument is missing
    AppArgs = new[] { "-abtest", "-cd" }
};

var optChar = 0;
while ((optChar = getopt.GetNextOpt(out var optArg)) != -1) {
    switch (optChar) {
        case getopt.MissingOptChar:
            Console.WriteLine("Encountered option with missing argument!");
            break; // in this mode, there is currently no mechanism to get the supplied option
        default:
            Console.WriteLine($"Got option '{ (char)optChar }' with argument \"{ (optArg ?? "null argument") }\"");
            break;
    }
}

// Output:
// => Got option 'a' with argument "null argument"
// => Got option 'b' with argument "test"
// => Got option 'c' with argument "null argument"
// => Encountered option with missing argument!

Example with IgnoreMissingArgument = false

var getopt = new GetOpt {
    ShortOpts = "ab:cd:",
    IgnoreMissingArgument = false, // GetNextOpt will throw a ParseException if an argument is missing
    AppArgs = new[] { "-abtest", "-cd" }
};

var optChar = 0;
do {
    string? optArg;
    try { optChar = getopt.GetNextOpt(out optArg); }
    catch (ParseException ex) {
        Console.WriteLine(ex.ToString());
        continue;
    }

    switch (optChar) {
        case -1: break;
        default:
            Console.WriteLine($"Got option '{ optChar }' with argument \"{ (optArg ?? "null argument") }\"");
    }
} while (optChar != -1);

// Output:
// => Got option 'a' with argument "null argument"
// => Got option 'b' with argument "test"
// => Got option 'c' with argument "null argument"
// => Error occurred while parsing 'd': Missing required argument

Parsing with long options

getopt.net supports with short and long options.

Short options are prefixed with a single dash (-), wheras long options are prefixed with a double-dash (--).

By default, getopt.net allows parsing both short options and long options.
The default behaviour is equivalent to calling getopt_long(). Unlike getopt_long(), the last element of Options must not be all zeros.

This behaviour can be modified, by setting getopt.ShortOptsOnly, which is now equivalent to calling getopt().

Not setting the ShortOpts property is equivalent to calling getopt_long_only().

Example only long opts:

var getopt = new GetOpt {
    Options = new[] {
        new Option { Name = "aoption", ArgumentType = ArgumentType.None, Value = 'a' },
        new Option { Name = "boption", ArgumentType = ArgumentType.Required, Value = 'b' },
        new Option { Name = "coption", ArgumentType = ArgumentType.Optional, Value = 'c' }
    },
    AppArgs = new[] { "--aoption", "--boption=test", "--coption", "test2", "somerandomvalue" }
};

var optChar = 0;
while ((optChar = getopt.GetNextOpt(out var optArg)) != -1) {
    switch (optChar) {
        case 'a':
            Console.WriteLine("Got --aoption");
            break;
        case 'b':
            Console.WriteLine($"Got --boption with argument \"{ optArg }\"");
            break;
        case 'c':
            if (string.IsNullOrEmpty(optArg)) {
                Console.WriteLine("Got --coption without argument.");
            } else {
                Console.WriteLine($"Got --coption with argument \"{ optArg }\"");
            }
            break;
        case '?': // equivalent to case GetOpt.MissingArgChar:
            Console.WriteLine("Got argument with missing char!");
            break;
        case 1:
            Console.WriteLine($"Got non-option arg \"{ optArg }\"");
            break;
    }
}

// Output:
// => Got --aoption
// => Got --boption with argument "test"
// => Got --coption with argument "test2"
// => Got non-option arg "somerandomvalue"

Fallback to long options!

If you've forgotten to add one of your Options to your ShortOpts, getopt.net has you covered!

If getopt.net encounteres a short option that isn't found in ShortOpts, it will double-check in Options to see if the option can be found there. It will the be parsed with with the options set there.

Example

var getopt = new GetOpt {
    ShortOpts = "ab:c",
    Options = new[] {
        new Option("aoption", ArgumentType.None, 'a'),
        new Option("boption", ArgumentType.Required, 'b'),
        new Option("coption", ArgumentType.Optional, 'c'),
        new Option("doption", ArgumentType.None, 'd') // this was not added to ShortOpts
    },
    AppArgs = new[] { "-dacbtest" }
};

var optChar = 0;

while ((optChar = getopt.GetNextOpt(out var optArg)) != -1) {
    Console.Write($"Got option '{ (char)optChar }' ");

    if (string.IsNullOrEmpty(optArg)) {
        Console.WriteLine("without optarg.");
    } else {
        Console.WriteLine($"with optarg \"{ optArg }\".");
    }
}

// Output:
// => Got option 'd' without optarg.
// => Got option 'a' without optarg.
// => Got option 'c' without optarg.
// => Got option 'b' with optarg "test".