21

I am writing a Java application that takes command line arguments which are processed using Apache Commons CLI with the GnuParser. For reasons that are not interesting to get into, I would like it to silently ignore unknown command line options instead of throwing a ParseException but I don't see a way to do that. I see that there is a stopAtNonOption boolean option on GnuParser.parse() but what I want is more like ignoreAtNonOption where it will keep processing options after encountering an unknown token.

I could implement my own parser to accomplish this but I'm surprised there isn't this functionality built in so I thought I'd check before going down that road.

Example code for what I'm talking about:

try {
  CommandLine commandLine = parser.parse(options, args);
  // stopAtNonOption set to true (below) is also not what I want
  // CommandLine commandLine = parser.parse(options, args, true);
} catch (ParseException e) {
  LOG.error("error parsing arguments", e);
  throw new RuntimeException(e);
}
skaffman
  • 398,947
  • 96
  • 818
  • 769
Michael Ridley
  • 10,378
  • 3
  • 22
  • 16

4 Answers4

29

This works for me (other parsers can be derived, too):

public class ExtendedGnuParser extends GnuParser {

    private boolean ignoreUnrecognizedOption;

    public ExtendedGnuParser(final boolean ignoreUnrecognizedOption) {
        this.ignoreUnrecognizedOption = ignoreUnrecognizedOption;
    }

    @Override
    protected void processOption(final String arg, final ListIterator iter) throws     ParseException {
        boolean hasOption = getOptions().hasOption(arg);

        if (hasOption || !ignoreUnrecognizedOption) {
            super.processOption(arg, iter);
        }
    }

}
stema
  • 90,351
  • 20
  • 107
  • 135
Pascal Schäfer
  • 306
  • 3
  • 3
  • 8
    That is beautiful, thanks! Btw this should be a note to any API designers as to why you absolutely positively really don't want to make your API classes final. Why? Because you are not smart enough to figure out all the ways client code would like to reuse your stuff. – James E. Ervin May 29 '12 at 18:50
  • I have done the same for BasicParser. Worked like a charm!. Should definitely be added to cli API. – Dudi Jul 18 '14 at 08:22
  • 1
    This is now Deprecated :(. – Decoded Feb 26 '16 at 04:55
  • @JamesE.Ervin Fair point re: final, but instead of extending you can always use the delegate pattern: https://en.wikipedia.org/wiki/Delegation_pattern – thetoolman Mar 03 '16 at 21:59
  • 1
    What if I want to have access to ignored options in runtime after parsing? – Aleksandr Kravets Aug 18 '16 at 09:45
8

As mentioned in a comment, the accepted solution is no more suitable because the processOption method has been deprecated and removed.

Here's my solution:

public class ExtendedParser extends DefaultParser {

    private final ArrayList<String> notParsedArgs = new ArrayList<>();

    public String[] getNotParsedArgs() {
        return notParsedArgs.toArray(new String[notParsedArgs.size()]);
    }

    @Override
    public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException {
        if(stopAtNonOption) {
            return parse(options, arguments);
        }
        List<String> knownArguments = new ArrayList<>();
        notParsedArgs.clear();
        boolean nextArgument = false;
        for (String arg : arguments) {
            if (options.hasOption(arg) || nextArgument) {
                knownArguments.add(arg);
            } else {
                notParsedArgs.add(arg);
            }

        nextArgument = options.hasOption(arg) && options.getOption(arg).hasArg();
        }
        return super.parse(options, knownArguments.toArray(new String[knownArguments.size()]));
    }

}

Compared with the solution proposed by Pascal, it also checks for options with arguments and it keeps not parsed args in a separate list.

SimoV8
  • 1,382
  • 1
  • 18
  • 32
1

This is not possible with Commons CLI. But there may be another way to achieve the result you expect if you give more details of your use case.

Emmanuel Bourg
  • 9,601
  • 3
  • 48
  • 76
0

I am a very bad developer, and I do this to break the code:

public class EasyPosixParser extends PosixParser {
    @Override
    protected void processOption(String arg, ListIterator iter) throws ParseException
    {
        try {
            super.processOption(arg, iter);
        } catch (ParseException e) {
            // do nothing
        }
    }
}

in your main code, you do:

    CommandLineParser commandlineParser = new EasyPosixParser();
Mark Hall
  • 53,938
  • 9
  • 94
  • 111
Kung Wang
  • 9
  • 1