Search code examples
javaapache-commonsapache-commons-cli

Apache Commons CLI 1.3.1: Option coming after another option with multiple arguments is consumed as ARGUMENT


I'm using Apache Commons CLI 1.3.1 to process some options and some of the options can take one to unlimited number arguments. A trivia example with two options would be like this

usage: myProgram -optionX <arg1> <arg2> <arg3> < ... > [-optionY]
-optionX <arg1> <arg2> <arg3> < ... >   optionX takes one to unlimited
                                        number of arguments.
-optionY                                optionY is optional

What I found is that the second option optionY is always recognized as an ARGUMENT of the optionX instead of being recognized as an OPTION by itself. That means if you type myProgram -optionX arg1 arg2 -optionY in the command line, you will get three arguments (arg1, arg2, and -optionY) associated with optionX.

Here is the code that anyone can use to reproduce the problem.

import org.apache.commons.cli.*;

public class TestCli {

public static void main(String[] args) {

    Option optObj1 = Option.builder("optionX")
                            .argName("arg1> <arg2> <arg3> < ... ")
                            .desc("optionX takes one to unlimited number of arguments.")
                            .required()
                            .hasArgs()
                            .build();

    Option optObj2 = new Option("optionY", "optionY is optional");

    Options optsList = new Options();
    optsList.addOption(optObj1);
    optsList.addOption(optObj2);

    CommandLineParser commandLineParser = new DefaultParser();

    try {

        CommandLine cmd = commandLineParser.parse(optsList, new String[]{"-optionX", "arg1", "arg2", "-optionY"});

        System.out.println("--------------------------");
        System.out.println("argument list of optionX: ");
        for (String argName : cmd.getOptionValues(optObj1.getOpt())) {
            System.out.println("arg: " + argName);
        }

        System.out.println("--------------------------");
        System.out.println("value of optionY: " + cmd.hasOption(optObj2.getOpt()));

    } catch (ParseException e) {
        System.out.println("Unexcepted option: " + e.getMessage());
    }

}
}

This is the output you'll see.

--------------------------
argument list of optionX: 
arg: arg1
arg: arg2
arg: -optionY
--------------------------
value of optionY: false

Do I miss something here?

Any suggestion will be really appreciated.


Solution

  • The problem is that you are putting the long name in the short name of the option.

    When you use Option optObj1 = Option.builder("optionX").... or new Option("optionY", "optionY is optional"), you're setting the short name of the option, which is supposed to be only 1 character long.

    That work well until you have a multi arguments option. In this case, the parser cannot find "o" (the first letter of your option) in his short option list and you don't have a long name set, so the parser determines that -optionY is just another argument for -optionX.

    To solve your problem, just set the long option name of your option and it should work alright.

    Example

    Option.builder("x").longOpt("optionX")....
    
    Option optObj2 = new Option("y", "optionY", hasArgs, "optionY is optional");