Define an option that has both a mandatory and optional argument

1.6k views Asked by At

I am currently trying to come up with a way to build a command-line option that when provided can either take one required argument or a second optional argument (in addition to the required one). I am trying to achieve this using apache commons cli.

i.e. myProgram -a [integer]

myProgram -a "test" --> isValid
myProgram -a "test" 2 --> also isValid

I have tried: Option.builder("a").hasArg().numberOfArgs(2).optionalArg(true).build();

&

Option.builder("a").hasArg().numberOfArgs(2).build();

Neither of which is working as desired. The first example makes both arguments optional and it allows for passing empty arguments which is against the requirements.

The second example makes both parameters required and fails when only the string type arg is provided.

I have looked at the documentation for commons cli but the usage cases they provided didnt touch complex cases like this one, and similarly did not get much details from the api docs.

I expect the program to fail when -a is provided with no value, but to succeed when provided with 1 or 2 arguments as shown previously.

3

There are 3 answers

4
Jimmy Potter On

According to my understanding of the documentation for appache-commons-cli (and I might be wrong), there doesn't seem to be a way to set "optionalArg" on a per-argument basis.

Also the way I see it used in the examples, it seems to take in arguments with separators in between, as in -D<property>=<value> instead of spaces? I'm unsure about this though.

But yeah, for what you want, you could do it dirty and allow optional arguments, then reject it when the option doesn't have the required argument (defeating the purpose of using commons-cli I know).

EDIT: Did you try using PatternOptionBuilder ? This example intrigues me:

For example, the following allows command line flags of '-v -p string-value -f /dir/file'. The exclamation mark precede a mandatory option.

 Options options = PatternOptionBuilder.parsePattern("vp:!f/");

It says the exclamation mark can be used before mandatory options, but I'm not sure if it can be used with mandatory arguments? I didn't try it though.

0
Adam Burke On

In the past, in these circumstances, I have added a second option. So in your example, -b.

-b [bvalue]   If -a is enabled, ensure that it uses bvalue to thinginate.

If your second (optional) value acts as a flag, it can simply be a boolean flag.

If your second (optional) value is some more arbitrary input, this second option can take a single required argument.

Though I am sure you have reasons for organizing your arguments as -a reqdval optval, it will often result in a more usable command line to not have these multiple layers of optionality anyway (ie the existence of a second place optional value within an option). You could think of it as the UX difference between a two-level and a three-level nested menu. With a limited number of items, the two-level menu is often more usable. (And without more specific details of your use case, answers have to be in generalities.)

0
lgott On

So you have an option that can be provided zero or one time, and can have one or two values?

final Options options = new Options()
            .addOption(Option.builder("a").hasArg().hasArgs().build());
CommandLine cmd = new DefaultParser().parse(options, args);
LOG.debug(Arrays.asList(args).toString() + " : " + Arrays.asList(cmd.getOptionValues("a")).toString());
DEBUG :: [-a, b] : [b]
DEBUG :: [-a, b, c] : [b, c]

You might want to raise your own parse exceptions when too many or too few arguments are provided.

Look at this answer if you want to provide -a multiple times: https://stackoverflow.com/a/64611857/8656281