添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

What if I wanted to parse this:

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3

And the result I want in my program is:

regular Java args[]  of size=4
org.apache.commons.cli.Options[]  of size=3
org.apache.commons.cli.Options[] #2 of size=1

I would prefer to use Apache Commons CLI, but the documentation is a little unclear about the case I present above. Specifically, the documentation doesn't tell you how to handle options of the 3rd type I specify below:

1. options with a "-" char
2. options with a "--" char
3. options without any marker, or "bare args"

I wish that Apache Commons CLI would work but STILL be able to pass regular args to the program if those args didn't have a option prefix. Maybe it does but the documentation doesnt say so as I read through it...

import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.Option.Builder; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.ParseException; public static void main(String[] parameters) CommandLine commandLine; Option option_A = Option.builder("A") .required(true) .desc("The A option") .longOpt("opt3") .build(); Option option_r = Option.builder("r") .required(true) .desc("The r option") .longOpt("opt1") .build(); Option option_S = Option.builder("S") .required(true) .desc("The S option") .longOpt("opt2") .build(); Option option_test = Option.builder() .required(true) .desc("The test option") .longOpt("test") .build(); Options options = new Options(); CommandLineParser parser = new DefaultParser(); String[] testArgs = { "-r", "opt1", "-S", "opt2", "arg1", "arg2", "arg3", "arg4", "--test", "-A", "opt3", }; options.addOption(option_A); options.addOption(option_r); options.addOption(option_S); options.addOption(option_test); commandLine = parser.parse(options, testArgs); if (commandLine.hasOption("A")) System.out.print("Option A is present. The value is: "); System.out.println(commandLine.getOptionValue("A")); if (commandLine.hasOption("r")) System.out.print("Option r is present. The value is: "); System.out.println(commandLine.getOptionValue("r")); if (commandLine.hasOption("S")) System.out.print("Option S is present. The value is: "); System.out.println(commandLine.getOptionValue("S")); if (commandLine.hasOption("test")) System.out.println("Option test is present. This is a flag option."); String[] remainder = commandLine.getArgs(); System.out.print("Remaining arguments: "); for (String argument : remainder) System.out.print(argument); System.out.print(" "); System.out.println(); catch (ParseException exception) System.out.print("Parse error: "); System.out.println(exception.getMessage()); some of the approaches in this answer are no longer supported. examples for current CLI version: commons.apache.org/proper/commons-cli/usage.html – stuart Feb 16, 2018 at 15:51

You could just do it manually.

NB: might be better to use a HashMap instead of an inner class for the opts.

/** convenient "-flag opt" combination */
private class Option {
     String flag, opt;
     public Option(String flag, String opt) { this.flag = flag; this.opt = opt; }
static public void main(String[] args) {
    List<String> argsList = new ArrayList<String>();  
    List<Option> optsList = new ArrayList<Option>();
    List<String> doubleOptsList = new ArrayList<String>();
    for (int i = 0; i < args.length; i++) {
        switch (args[i].charAt(0)) {
        case '-':
            if (args[i].length < 2)
                throw new IllegalArgumentException("Not a valid argument: "+args[i]);
            if (args[i].charAt(1) == '-') {
                if (args[i].length < 3)
                    throw new IllegalArgumentException("Not a valid argument: "+args[i]);
                // --opt
                doubleOptsList.add(args[i].substring(2, args[i].length));
            } else {
                if (args.length-1 == i)
                    throw new IllegalArgumentException("Expected arg after: "+args[i]);
                // -opt
                optsList.add(new Option(args[i], args[i+1]));
            break;
        default:
            // arg
            argsList.add(args[i]);
            break;
    // etc
                I was just typing that in!  You could also use a hash table instead of a switch statement if you'd like to be fancy.
– IslandCow
                Sep 7, 2011 at 23:52
                What do you mean by "-flag opt combination"?  Do you mean a dash character followed by any number of chars?
– djangofan
                Sep 8, 2011 at 18:18
                Ok, ill accept this as the answer although I think optsLists should be converted to HashMap .
– djangofan
                Sep 8, 2011 at 18:57
                In for loop you need to initialize the iterator (int i = 0;), otherwise compiler yells at this (Java 7).
– theUg
                Nov 17, 2013 at 23:15

I like this one. Simple, and you can have more than one parameter for each argument:

final Map<String, List<String>> params = new HashMap<>();
List<String> options = null;
for (int i = 0; i < args.length; i++) {
    final String a = args[i];
    if (a.charAt(0) == '-') {
        if (a.length() < 2) {
            System.err.println("Error at argument " + a);
            return;
        options = new ArrayList<>();
        params.put(a.substring(1), options);
    else if (options != null) {
        options.add(a);
    else {
        System.err.println("Illegal parameter usage");
        return;

For example:

-arg1 1 2 --arg2 3 4
System.out.print(params.get("arg1").get(0)); // 1
System.out.print(params.get("arg1").get(1)); // 2
System.out.print(params.get("-arg2").get(0)); // 3
System.out.print(params.get("-arg2").get(1)); // 4
                I'm surprised there's no library out there that does it this elegant way. All the others compromise your code in one way or another (redundant code, mutable state, additional constructor calls).
– Sridhar Sarnobat
                Oct 7, 2015 at 22:48
                Enhancement request - can you modify this answer to accept an equals delimiter? :) I'd guess myself but rather you do it.
– Sridhar Sarnobat
                Oct 8, 2015 at 0:47

I realize that the question mentions a preference for Commons CLI, but I guess that when this question was asked, there was not much choice in terms of Java command line parsing libraries. But nine years later, in 2020, would you not rather write code like the below?

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.File;
import java.util.List;
import java.util.concurrent.Callable;
@Command(name = "myprogram", mixinStandardHelpOptions = true,
  description = "Does something useful.", version = "1.0")
public class MyProgram implements Callable<Integer> {
    @Option(names = "-r", description = "The r option") String rValue;
    @Option(names = "-S", description = "The S option") String sValue;
    @Option(names = "-A", description = "The A file") File aFile;
    @Option(names = "--test", description = "The test option") boolean test;
    @Parameters(description = "Positional params") List<String> positional;
    @Override
    public Integer call() {
        System.out.printf("-r=%s%n", rValue);
        System.out.printf("-S=%s%n", sValue);
        System.out.printf("-A=%s%n", aFile);
        System.out.printf("--test=%s%n", test);
        System.out.printf("positionals=%s%n", positional);
        return 0;
    public static void main(String... args) {
        System.exit(new CommandLine(new MyProgram()).execute(args));

Execute by running the command in the question:

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3

What I like about this code is that it is:

  • compact - no boilerplate
  • declarative - using annotations instead of a builder API
  • strongly typed - annotated fields can be any type, not just String
  • no duplication - option declaration and getting parse result are together in the annotated field
  • clear - the annotations express the intention better than imperative code
  • separation of concerns - the business logic in the call method is free of parsing-related logic
  • convenient - one line of code in main wires up the parser and runs the business logic in the Callable
  • powerful - automatic usage and version help with the built-in --help and --version options
  • user-friendly - usage help message uses colors to contrast important elements like option names from the rest of the usage help to reduce the cognitive load on the user
  • The above functionality is only part of what you get when you use the picocli (https://picocli.info) library.

    Now, bear in mind that I am totally, completely, and utterly biased, being the author of picocli. :-) But I do believe that in 2020 we have better alternatives for building a command line apps than Commons CLI.

    Here is @DwB solution upgraded to Commons CLI 1.3.1 compliance (replaced deprecated components OptionBuilder and GnuParser). The Apache documentation uses examples that in real life have unmarked/bare arguments but ignores them. Thanks @DwB for showing how it works.

    import org.apache.commons.cli.CommandLine;
    import org.apache.commons.cli.CommandLineParser;
    import org.apache.commons.cli.DefaultParser;
    import org.apache.commons.cli.HelpFormatter;
    import org.apache.commons.cli.Option;
    import org.apache.commons.cli.Options;
    import org.apache.commons.cli.ParseException;
    public static void main(String[] parameters) {
        CommandLine commandLine;
        Option option_A = Option.builder("A").argName("opt3").hasArg().desc("The A option").build();
        Option option_r = Option.builder("r").argName("opt1").hasArg().desc("The r option").build();
        Option option_S = Option.builder("S").argName("opt2").hasArg().desc("The S option").build();
        Option option_test = Option.builder().longOpt("test").desc("The test option").build();
        Options options = new Options();
        CommandLineParser parser = new DefaultParser();
        options.addOption(option_A);
        options.addOption(option_r);
        options.addOption(option_S);
        options.addOption(option_test);
        String header = "               [<arg1> [<arg2> [<arg3> ...\n       Options, flags and arguments may be in any order";
        String footer = "This is DwB's solution brought to Commons CLI 1.3.1 compliance (deprecated methods replaced)";
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("CLIsample", header, options, footer, true);    
        String[] testArgs =
                { "-r", "opt1", "-S", "opt2", "arg1", "arg2",
                        "arg3", "arg4", "--test", "-A", "opt3", };
            commandLine = parser.parse(options, testArgs);
            if (commandLine.hasOption("A"))
                System.out.print("Option A is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("A"));
            if (commandLine.hasOption("r"))
                System.out.print("Option r is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("r"));
            if (commandLine.hasOption("S"))
                System.out.print("Option S is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("S"));
            if (commandLine.hasOption("test"))
                System.out.println("Option test is present.  This is a flag option.");
                String[] remainder = commandLine.getArgs();
                System.out.print("Remaining arguments: ");
                for (String argument : remainder)
                    System.out.print(argument);
                    System.out.print(" ");
                System.out.println();
        catch (ParseException exception)
            System.out.print("Parse error: ");
            System.out.println(exception.getMessage());
    

    Output:

    usage: CLIsample [-A <opt3>] [-r <opt1>] [-S <opt2>] [--test]
                     [<arg1> [<arg2> [<arg3> ...
           Options, flags and arguments may be in any order
     -A <opt3>   The A option
     -r <opt1>   The r option
     -S <opt2>   The S option
        --test   The test option
    This is DwB's solution brought to Commons CLI 1.3.1 compliance (deprecated
    methods replaced)
    Option A is present.  The value is: opt3
    Option r is present.  The value is: opt1
    Option S is present.  The value is: opt2
    Option test is present.  This is a flag option.
    Remaining arguments: arg1 arg2 arg3 arg4
    

    You could use https://github.com/jankroken/commandline , here's how to do that:

    To make this example work, I must make assumptions about what the arguments means - just picking something here...

    -r opt1 => replyAddress=opt1
    -S opt2 arg1 arg2 arg3 arg4 => subjects=[opt2,arg1,arg2,arg3,arg4]
    --test = test=true (default false)
    -A opt3 => address=opt3
    

    this can then be set up this way:

    public class MyProgramOptions {
      private String replyAddress;
      private String address;
      private List<String> subjects;
      private boolean test = false;
      @ShortSwitch("r")
      @LongSwitch("replyAddress") // if you also want a long variant. This can be skipped
      @SingleArgument
      public void setReplyAddress(String replyAddress) {
        this.replyAddress = replyAddress;
      @ShortSwitch("S")
      @AllAvailableArguments
      public void setSubjects(List<String> subjects) {
        this.subjects = subjects;
      @LongSwitch("test")
      @Toggle(true)
      public void setTest(boolean test) {
        this.test = test;
      @ShortSwitch("A")
      @SingleArgument
      public void setAddress(String address) {
        this.address = address;
      // getters...
    

    and then in the main method, you can just do:

    public final static void main(String[] args) {
      try {
        MyProgramOptions options = CommandLineParser.parse(MyProgramOptions.class, args, OptionStyle.SIMPLE);
        // and then you can pass options to your application logic...
      } catch
    

    You could use the refcodes-console artifact at refcodes-console on REFCODES.ORG:

    Option<String> r     = new StringOptionImpl( "-r", null, "opt1", "..." );
    Option<String> s     = new StringOptionImpl( "-S", null, "opt2", "..." );
    Operand<String> arg1 = new StringOperandImpl( "arg1", "..." );
    Operand<String> arg2 = new StringOperandImpl( "arg2", "..." );
    Operand<String> arg3 = new StringOperandImpl( "arg3", "..." );
    Operand<String> arg4 = new StringOperandImpl( "arg4", "..." );
    Switch test          = new SwitchImpl( null, "--test", "..." );
    Option<String> a     = new StringOptionImpl( "-A", null, "opt3", "..." );
    Condition theRoot    = new AndConditionImpl( r, s, a, arg1, arg2, arg3, arg4,
        test );
    

    Create your arguments parser ArgsParserImpl with your root condition:

    ArgsParser theArgsParser = new ArgsParserImpl( theRoot );
    theArgsParser.setName( "MyProgramm" );
    theArgsParser.setSyntaxNotation( SyntaxNotation.GNU_POSIX );
    

    Above you define your syntax, below you invoke the parser:

    theArgsParser.printUsage();
    theArgsParser.printSeparatorLn();
    theArgsParser.printOptions();
    theArgsParser.evalArgs( new String[] {
        "-r", "RRRRR", "-S", "SSSSS", "11111", "22222", "33333", "44444", 
        "--test", "-A", "AAAAA"
    

    In case you provided some good descriptions, theArgsParser.printUsage() will even show you the pretty printed usage:

    Usage: MyProgramm -r <opt1> -S <opt2> -A <opt3> arg1 arg2 arg3 arg4 --test
      

    In the above example all defined arguments must be passed by the user, else the parser will detect a wrong usage. In case the --test switch is to be optional (or any other argument), assign theRoot as follows:

    theRoot = new AndConditionImpl( r, s, a, arg1, arg2, arg3, arg4, new OptionalImpl( test ) );

    Then your syntax looks as follows:

    Usage: MyProgramm -r <opt1> -S <opt2> -A <opt3> arg1 arg2 arg3 arg4 [--test]
    

    The full example for your case you find in the StackOverFlowExamle. You can use AND, OR, XOR conditions and any kind of nesting ... hope this helps.

    Evaluate the parsed arguments as follows: r.getValue() ); or if (test.getValue() == true) ...:

    LOGGER.info( "r    :=" + r.getValue() );
    LOGGER.info( "S    :=" + s.getValue() );
    LOGGER.info( "arg1 :=" + arg1.getValue() );
    LOGGER.info( "arg2 :=" + arg2.getValue() );
    LOGGER.info( "arg3 :=" + arg3.getValue() );
    LOGGER.info( "arg4 :=" + arg4.getValue() );
    LOGGER.info( "test :=" + test.getValue() + "" );
    LOGGER.info( "A    :=" + a.getValue() );
                    You are right. I fixed this with version 1.1.0 released just now to maven central [1]. The compile time dependencies excluding the refcodes dependencies[2] I determine with this command:  mvn dependency:list -DincludeScope=compile -DexcludeScope=test | grep -v refcodes  Removing one false positive [3] from the result, I end up with those dependencies for version 1.1.0:  jline:jline:jar:2.14.2:compile log4j:log4j:jar:1.2.17:compile  Thanks for the input & hope it helps! :-)
    – REFCODES.ORG
                    Sep 4, 2016 at 13:01
                    [1] See the refcodes change-list for version 1.1.0.  [2] Them refcodes artifacts are of one source, one version, all released in sync and split into modules as of separation of concerns and modularization, moreover all are located at bitbucket.org/refcodes.  [3] See Why does dependency:list ... lists compile scope for transitive dependencies of test scope items
    – REFCODES.ORG
                    Sep 4, 2016 at 13:04
      public static void main(String[] args) {
         List<String> argsList  = new ArrayList<String>();  
         List<String> optsList  = new ArrayList<String>();
         List<String> doubleOptsList  = new ArrayList<String>();
         for (int i=0; i < args.length; i++) {
             switch (args[i].charAt(0)) {
             case '-':
                 if (args[i].charAt(1) == '-') {
                     int len = 0;
                     String argstring = args[i].toString();
                     len = argstring.length();
                     System.out.println("Found double dash with command " +
                         argstring.substring(2, len) );
                     doubleOptsList.add(argstring.substring(2, len));           
                 } else {
                     System.out.println("Found dash with command " + 
                       args[i].charAt(1) + " and value " + args[i+1] );   
                     i= i+1;
                     optsList.add(args[i]);      
             break;         
             default:            
             System.out.println("Add a default arg." );
             argsList.add(args[i]);
             break;         
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.