Search code examples
javacommand-line-interfaceapache-commons-cli

Commons CLI is not honoring my command line setup


Using Apache Commons CLI 1.2 here. I have an executable JAR that needs to take 2 runtime options, fizz and buzz; both are strings that require arguments/values. I would like (if at all possible) my app to be executed like so:

java -jar myapp.jar -fizz "Alrighty, then!" -buzz "Take care now, bye bye then!"

In this case, the value for the fizz option would be "Alrighty, then!", etc.

Here's my code:

public class MyApp {
    private Options cmdLineOpts = new Options();
    private CommandLineParser cmdLineParser = new GnuParser();
    private HelpFormatter helpFormatter = new HelpFormatter();

    public static void main(String[] args) {
        MyApp myapp = new MyApp();
        myapp.processArgs(args);
    }

    private void processArgs(String[] args) {
        Option fizzOpt = OptionBuilder
            .withArgName("fizz")
            .withLongOpt("fizz")
            .hasArg()
            .withDescription("The fizz argument.")
            .create("fizz");

        Option buzzOpt = OptionBuilder
            .withArgName("buzz")
            .withLongOpt("buzz")
            .hasArg()
            .withDescription("The buzz argument.")
            .create("buzz");

        cmdLineOpts.addOption(fizzOpt);
        cmdLineOpts.addOption(buzzOpt);

        CommandLine cmdLine;

        try {
            cmdLine = cmdLineParser.parse(cmdLineOpts, args);

            // Expecting to get a value of "Alright, then!"
            String fizz = cmdLine.getOptionValue("fizz");
            System.out.println("Fizz is: " + fizz);
        } catch(ParseException parseExc) {
            helpFormatter.printHelp("myapp", cmdLineOpts, true);
            throw parseExc;
        }
    }
}

When I run this I get the following output:

Fizz is: null

What do I need to do to my code so that my app can be invoked the way I want it to? Or what's the closest I can get to it?

Bonus points: If someone can explain to me the difference between the OptionBuilder's withArgName(...), withLongOpt(...) and create(...) arguments, as I am passing in the same value for them all like so:

Option fizzOpt = OptionBuilder
    .withArgName("fizz")
    .withLongOpt("fizz")    }   Why do I have to pass the same value in 3 times to make this work?!?
    .create("fizz");

Solution

  • First the .hasArg() on your OptionBuilder tells it that you expect an argument after the paramter flag.

    I got it to work with this command line

    --fizz "VicFizz is good for you" -b "VicBuzz is also good for you"
    

    Using the following code - I put this in the constructor

    Option fizzOpt = OptionBuilder
            .withArgName("Fizz")
            .withLongOpt("fizz")
            .hasArg()
            .withDescription("The Fizz Option")
            .create("f");
    
    cmdLineOpts.addOption(fizzOpt);
    cmdLineOpts.addOption("b", true, "The Buzz Option");
    

    Breakdown

    The option settings are necessary in order to provide more usability on the command line, as well as a nice usage message (see below)

    • .withArgName("Fizz"): Gives your argument a nice title in the usage (see below)
    • .withLongOpt("fizz"): allows for --fizz "VicFizz is good for you"
    • .create("f"): is the main parameter and allows command line -f "VicFizz is good for you"
    • Notice that Option b for fuzz was constructed much simpler, sacrificing readability during usage

    Usage Message

    Personally I love CLI programs that print out a nice usage. You can do this with the HelpFormatter. For example:

    private void processArgs(String[] args) {
      if (args == null || args.length == ) {
        helpFormatter.printHelp("Don't you know how to call the Fizz", cmdLineOpts);
        ...
    

    This will print something usefull like:

    usage: Don't you know how to call the Fizz
      -b <arg>          The Buzz Option
      -f,--fizz <Fizz>  The Fizz Option
    

    Notice how a the short option -f, the long option --fizz, and a name <Fizz> is displayed, along with the description.

    Hope this helps