解析Java命令行程序的参数

dja*_*fan 49 java command-line-interface args

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

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3
Run Code Online (Sandbox Code Playgroud)

我想在我的程序中得到的结果是:

regular Java args[]  of size=4
org.apache.commons.cli.Options[]  of size=3
org.apache.commons.cli.Options[] #2 of size=1
Run Code Online (Sandbox Code Playgroud)

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

1. options with a "-" char
2. options with a "--" char
3. options without any marker, or "bare args"
Run Code Online (Sandbox Code Playgroud)

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

DwB*_*DwB 62

使用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());
        }
    }

Run Code Online (Sandbox Code Playgroud)

  • 不再支持此答案中的某些方法.当前CLI版本的示例:https://commons.apache.org/proper/commons-cli/usage.html (2认同)

Cha*_*win 32

你可以手动完成.

注意:对于opts,使用HashMap而不是内部类可能更好.

/** 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
}
Run Code Online (Sandbox Code Playgroud)

  • 好吧,虽然我认为optsLists应该转换为HashMap,但是接受这个作为答案. (2认同)

use*_*877 14

我喜欢这一个.很简单,每个参数都有多个参数:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

例如:

-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
Run Code Online (Sandbox Code Playgroud)

  • 我很惊讶没有图书馆能以这种优雅的方式做到这一点。所有其他因素都会以一种或另一种方式损害您的代码(冗余代码、可变状态、额外的构造函数调用)。 (2认同)

Rem*_*pma 6

我意识到这个问题提到了对 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


Pet*_*rio 5

这是@DwB解决方案,已升级到Commons CLI 1.3.1兼容性(已弃用的组件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());
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)