63

如果我想解析这个怎么办:

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

我在我的程序中想要的结果是:

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

我更喜欢使用Apache Commons CLI,但文档对我上面介绍的案例有点不清楚。具体来说,文档没有告诉您如何处理我在下面指定的第三种类型的选项:

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

我希望 Apache Commons CLI 能够工作,但如果这些 args 没有选项前缀,仍然能够将常规 args 传递给程序。也许确实如此,但是当我通读它时,文档并没有这么说...

4

9 回答 9

77

使用Apache Commons CLI 库commandline.getArgs() 获取 arg1、arg2、arg3 和 arg4。这是一些代码:



    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);

        try
        {
            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());
        }
    }

于 2011-09-08T00:41:11.223 回答
35

你可以手动完成。

注意:使用 HashMap 而不是 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]));
                i++;
            }
            break;
        default:
            // arg
            argsList.add(args[i]);
            break;
        }
    }
    // etc
}
于 2011-09-07T23:50:31.437 回答
19

我喜欢这一个。很简单,每个参数可以有多个参数:

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;
    }
}

例如:

-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
于 2014-10-15T07:11:21.723 回答
7

这是升级到 Commons CLI 1.3.1 合规性的@DwB 解决方案(替换了已弃用的组件 OptionBuilder 和 GnuParser)。Apache 文档使用了在现实生活中具有未标记/裸参数但忽略它们的示例。感谢@DwB 展示它是如何工作的。

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", };

    try
    {
        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());
    }

}

输出:

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
于 2016-04-14T20:20:14.190 回答
7

我意识到这个问题提到了对 Commons CLI 的偏好,但我想当这个问题被问到时,在 Java 命令行解析库方面没有太多选择。但是九年后的 2020 年,你会不会宁愿写下面这样的代码?

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));
    }
}

通过运行问题中的命令来执行:

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

我喜欢这段代码的地方在于:

  • 紧凑 - 没有样板
  • 声明式 - 使用注释而不是构建器 API
  • 强类型 - 带注释的字段可以是任何类型,而不仅仅是字符串
  • 没有重复 - 选项声明和获取解析结果一起在带注释的字段中
  • 清晰 - 注释比命令式代码更能表达意图
  • 关注点分离——方法中的业务逻辑call没有解析相关的逻辑
  • 方便 - main 中的一行代码连接解析器并在 Callable 中运行业务逻辑
  • 强大 - 内置--help--version选项的自动使用和版本帮助
  • 用户友好 - 使用帮助消息使用颜色将重要元素(如选项名称)与其他使用帮助进行对比,以减少用户的认知负担

上述功能只是您在使用 picocli ( https://picocli.info ) 库时获得的部分功能。

现在,请记住,作为 picocli 的作者,我完全、完全、完全有偏见。:-) 但我确实相信,在 2020 年,我们在构建命令行应用程序方面有比 Commons CLI 更好的选择。

于 2020-03-10T10:35:18.193 回答
2

您可以使用https://github.com/jankroken/commandline,这里是如何做到这一点:

为了使这个例子有效,我必须对参数的含义做出假设 - 只是在这里选择一些东西......

-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

然后可以这样设置:

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...
}

然后在 main 方法中,你可以这样做:

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
    ...
  }
}
于 2014-03-08T03:23:06.287 回答
1

java中命令行的简单代码:

class CMDLineArgument
{
    public static void main(String args[])
    {
        String name=args[0];
        System.out.println(name);
    }
}
于 2014-02-09T13:41:44.967 回答
1

您可以在 REFCODES.ORG 上的refcodes-consolerefcodes-console上使用该工件:

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 );

ArgsParserImpl使用您的根条件创建参数解析器:

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

上面定义语法,下面调用解析器:

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

如果您提供了一些好的描述,theArgsParser.printUsage()甚至会向您展示漂亮的打印用法:

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

在上面的示例中,所有定义的参数都必须由用户传递,否则解析器将检测到错误的用法。如果--test开关是可选theRoot的(或任何其他参数),请按如下方式分配:

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

然后您的语法如下所示:

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

您可以在StackOverFlowExamle中找到适用于您的案例的完整示例。您可以使用 AND、OR、XOR 条件和任何类型的嵌套......希望这会有所帮助。

评估解析的参数如下:r.getValue() );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() );
于 2015-04-06T10:23:19.030 回答
0

好的,感谢 Charles Goodwin 的概念。这是答案:

import java.util.*;
public class Test {

  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;         
         }     
     } 
  } 

}
于 2011-09-08T00:16:52.503 回答