如何在Java中解析命令行参数?


586

在Java中解析命令行参数的好方法是什么?


4
请参阅args4j和如何使用它的详细示例:martin-thoma.com/how-to-parse-command-line-arguments-in-java
Martin Thoma

看来我参加这个聚会已经很晚了,但是我为Java编写了一个命令行参数处理程序,并将其放在GitHub上:MainArgsHandler。至于关闭的线程,我认为这是一个非常有用的线程,但是应该将其迁移到Stack Exchange Programmers网站上进行常规编程讨论。
很棒的2014年

@RedGlyph-SO / SE似乎需要简化其规则。问题应该是:How to parse java command line arguments?。但是没有人真正想编写代码来执行此操作,而是使用一种工具。但是,寻找工具之类的工具并不是建设性的:(
AlikElzin-kilaka,2015年

6
投票重新开放。@AlikElzin:确实,他们确实需要审查其审核过程。我怀疑有一个徽章可以解决如此多的问题,并且它诱使想要成为主持人的人过分热心。
RedGlyph 2015年

8
这个问题是一个错误/单行答案和工具建议的蜜罐。它应该保持关闭状态。
2015年

Answers:


405

检查这些:

或自己动手:


例如,这是commons-cli解析2个字符串参数的方法:

import org.apache.commons.cli.*;

public class Main {


    public static void main(String[] args) throws Exception {

        Options options = new Options();

        Option input = new Option("i", "input", true, "input file path");
        input.setRequired(true);
        options.addOption(input);

        Option output = new Option("o", "output", true, "output file");
        output.setRequired(true);
        options.addOption(output);

        CommandLineParser parser = new DefaultParser();
        HelpFormatter formatter = new HelpFormatter();
        CommandLine cmd;

        try {
            cmd = parser.parse(options, args);
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            formatter.printHelp("utility-name", options);

            System.exit(1);
        }

        String inputFilePath = cmd.getOptionValue("input");
        String outputFilePath = cmd.getOptionValue("output");

        System.out.println(inputFilePath);
        System.out.println(outputFilePath);

    }

}

从命令行使用:

$> java -jar target/my-utility.jar -i asd                                                                                       
Missing required option: o

usage: utility-name
 -i,--input <arg>    input file path
 -o,--output <arg>   output file

40
请注意,与许多其他Apache库不同,Apache CLI没有依赖项。
弗拉基米尔·朱热夫

9
许多apache-common项目的缺点是,它们得到的提交越来越少,最终被淘汰。
Brett Ryan

4
这是Apache CLI项目的“使用情况”页面,详细介绍了如何快速开始使用它:commons.apache.org/cli/usage.html
Brad Parks

我不认为除了您定义接口并为方法添加注释之外,没有一个与Args4J / JCommander完全相同的代码吗?我从来没有能够喜欢那些“怪异的初始化”私人领域的课程……
Trejkaz 2015年

5
@RemkoPopma,您的picocli库看起来很棒,真的谢谢您。但是我认为您在这里和其他帖子中所做的事情(编辑已接受的答案并在其顶部提升您的图书馆,甚至没有透露这不是该帖子的原始作者的编辑,而是您的图书馆),这是对您的审核能力的可怕滥用。将其标记为其他mod。
亚历山大·马拉霍夫

311

看一下最近的JCommander

我创造了它。我很高兴收到问题或功能要求。


7
很高兴您喜欢JCommander :-)我不想为标记的处理添加太多语义,因此您只需要在使用的注释中添加同义词:@Parameter(names = {“ -h”,“- -help“})我认为这是一个合理的妥协。
Cedric Beust

14
很棒的工具。强大,灵活,您不必处理恼人的传统选项解析器。
伊恩·吉勒姆(IanGilham)2011年

3
是的,我想我会以与编写JCommander完全相同的方式编写我自己的命令行参数解析器。做得好。
SRG 2012年

9
@CedricBeust,这是一个很棒的库,非常感谢。由于我们可以定义自己的Args类,因此可以在不依赖库类的情况下进行传递,因此它非常灵活。
布雷特·瑞安

2
把比赛从水里吹出来!
Marcus Junius Brutus

235

7
@Ben Flynn嘿,里面有一些非常令人惊讶和有趣的异形车轮。我猜这是一种最无害的方式,可以证明这样做的方式不止一种!
lexicalscope

14
我注意到JOpt Simple的作者保持了非常相似的清单!我们需要的文字是将这些列表变成表格,列出功能和兴趣点,以便我们的贫穷用户可以做出明智的选择。
汤姆·安德森

1
我已经建立了罗普 - github.com/ryenus/rop,它具有基于注解的解决方案,你通过普通类和字段声明命令和选项,几乎是一个声明的方式来建立的命令行解析器。它可以构建类似应用程序的Git(单cmd)或Maven(多cmd)。
ryenus

8
列出的大多数项目基本上都是废弃软件。在浏览完列表之后,我想说的是,积极维护并广受欢迎的大人物,在cli,jcommander,args4j,jopt-simple和picocli看来很常见。向诸如argparse4j和cli-parser之类的作者表示歉意-我不得不做出一个比较武断的排名,并选择了前五名,显然列表中的其他项目很受欢迎,并且仍在积极开发中。
乔治·霍金斯

2
我向某人提出挑战,要求包括每个解析器的最后稳定版本的日期。
雪橇

50

现在是2020年,是时候比Commons CLI做得更好了... :-)

您应该构建自己的Java命令行解析器还是使用库?

许多类似小型实用程序的应用程序可能会滚动自己的命令行解析,以避免额外的外部依赖性。picocli可能是一个有趣的选择。

Picocli是一个现代的库和框架,可轻松构建功能强大,用户友好且启用GraalVM的命令行应用程序。它位于1个源文件中,因此应用程序可以将其包含为源文件以避免添加依赖项。

它支持颜色,自动补全,子命令等。用Java编写,可用于Groovy,Kotlin,Scala等。

最少使用ANSI颜色帮助

特征:

  • 基于注释:声明式避免重复表达程序员的意图
  • 方便:解析用户输入并使用一行代码运行您的业务逻辑
  • 严格键入所有内容-命令行选项以及位置参数
  • POSIX集群短选项(<command> -xvfInputFile以及<command> -x -v -f InputFile
  • 细粒度控制:允许最小,最大和可变数量的参数(例如"1..*""3..5"
  • 子命令(可以嵌套到任意深度)
  • 功能丰富:可组合的arg组,拆分带引号的arg,可重复的子命令等等
  • 用户友好:使用帮助消息使用颜色与其他使用帮助中的重要元素(如选项名称)进行对比,以减轻用户的认知负担
  • 将您的应用作为GraalVM本机映像分发
  • 适用于Java 5及更高版本
  • 详尽细致的文档

使用帮助消息很容易通过注释进行自定义(无需编程)。例如:

扩展使用帮助信息来源

我忍不住要再添加一个屏幕截图,以显示可能的使用帮助消息。使用帮助是您应用程序的面目,因此请发挥创造力并尽情玩乐!

picocli演示

免责声明:我创建了picocli。反馈或问题非常欢迎。


5
天才!这个答案被埋在了底部真是可惜。Apache Commons CLI冗长,有错误,并且很长时间没有更新。而且我不想使用Google的CLI解析器,因为我不想基于我的命令行参数使用历史记录来定向广告。但无论如何,它看起来比picocli更为冗长。
皮特

2
我在这里第二个@Pete ...我遍历了上面的列表,这完全是在浪费时间,而将其埋在底部。这应该是最大的答案。做得好!apache CLI或大多数其他解析器无法满足我的要求。他们甚至对picocli都充满挑战,但是它能够给我最接近我想要的语法/行为的信息,并且足够灵活,可以破解我真正需要的东西。作为奖励,它非常感谢ANSI的东西。
Shai Almog,

@ShaiAlmog投票最多的答案是10岁且已过时。我同意在2019年推荐Commons CLI会误导恕我直言。请考虑重写最重要的答案,以使其更符合最新情况。
雷姆·波马

我看到您已经尝试过了,我也不认为这对我也有用...如果您选择了1Picoli这样的名称,我们可以按字母顺序对第三个答案进行排序;-)
Shai Almog,

我认为我的更改被撤消的原因是我没有透露我的隶属关系(我是作者)。其他人不应该有这个问题。
雷姆·波马

23

我使用过JOpt并发现它非常方便:http ://jopt-simple.sourceforge.net/

首页还提供了大约8个替代库的列表,将其检出并选择最适合您需要的一个。


21

最近有人将我指向基于注释的args4j。我很喜欢!


1
为Args4J +1!极其人性化,灵活且易于理解。我认为它应该是构建Java CLI应用程序的标准库。
Zearin

它可以处理JCommander无法处理的无序(按字段顺序排序)使用情况打印,这很不错,并且更加灵活。
丹尼尔·哈里(DanielHári)'17年

@DanielHári仅作为参考,此功能已在JCommander中添加(大约在2017年2月下旬)。
内森

9

这是Google命令行解析库,是Bazel项目的一部分,是开源的。我个人认为这是目前最好的解决方案,并且比Apache CLI容易得多。

https://github.com/pcj/google-options

安装

淡褐色

maven_jar(
    name = "com_github_pcj_google_options",
    artifact = "com.github.pcj:google-options:jar:1.0.0",
    sha1 = "85d54fe6771e5ff0d54827b0a3315c3e12fdd0c7",
)

摇篮

dependencies {
  compile 'com.github.pcj:google-options:1.0.0'
}

马文

<dependency>
  <groupId>com.github.pcj</groupId>
  <artifactId>google-options</artifactId>
  <version>1.0.0</version>
</dependency>

用法

创建一个扩展OptionsBase并定义您的类的类@Option

package example;

import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;

import java.util.List;

/**
 * Command-line options definition for example server.
 */
public class ServerOptions extends OptionsBase {

  @Option(
      name = "help",
      abbrev = 'h',
      help = "Prints usage info.",
      defaultValue = "true"
    )
  public boolean help;

  @Option(
      name = "host",
      abbrev = 'o',
      help = "The server host.",
      category = "startup",
      defaultValue = ""
  )
  public String host;

  @Option(
    name = "port",
    abbrev = 'p',
    help = "The server port.",
    category = "startup",
    defaultValue = "8080"
    )
    public int port;

  @Option(
    name = "dir",
    abbrev = 'd',
    help = "Name of directory to serve static files.",
    category = "startup",
    allowMultiple = true,
    defaultValue = ""
    )
    public List<String> dirs;

}

解析参数并使用它们。

package example;

import com.google.devtools.common.options.OptionsParser;
import java.util.Collections;

public class Server {

  public static void main(String[] args) {
    OptionsParser parser = OptionsParser.newOptionsParser(ServerOptions.class);
    parser.parseAndExitUponError(args);
    ServerOptions options = parser.getOptions(ServerOptions.class);
    if (options.host.isEmpty() || options.port < 0 || options.dirs.isEmpty()) {
      printUsage(parser);
      return;
    }

    System.out.format("Starting server at %s:%d...\n", options.host, options.port);
    for (String dirname : options.dirs) {
      System.out.format("\\--> Serving static files at <%s>\n", dirname);
    }
  }

  private static void printUsage(OptionsParser parser) {
    System.out.println("Usage: java -jar server.jar OPTIONS");
    System.out.println(parser.describeOptions(Collections.<String, String>emptyMap(),
                                              OptionsParser.HelpVerbosity.LONG));
  }

}

https://github.com/pcj/google-options


嗨,保罗。当我阅读您的答案或您的项目文档时,我不知道它可以处理哪种命令行。例如,您可以提供myexecutable -c file.json -d 42 --outdir ./out。而且我看不到您如何定义短/多/描述选项...干杯
olibre



7

我知道这里的大多数人都会找到1000万个理由,他们为什么不喜欢我的方式,但没关系。我喜欢使事情保持简单,因此我只是使用'='将键与值分开,并将其存储在HashMap中,如下所示:

Map<String, String> argsMap = new HashMap<>();
for (String arg: args) {
    String[] parts = arg.split("=");
    argsMap.put(parts[0], parts[1]);
} 

您总是可以维护一个包含期望参数的列表,以帮助用户忘记参数或使用了错误的参数。但是,如果您想要太多的功能,那么该解决方案就不适合您。







4

如果您已经在使用Spring Boot,则可以直接进行参数解析。

如果要在启动后运行某些程序,请实现ApplicationRunner接口:

@SpringBootApplication
public class Application implements ApplicationRunner {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(ApplicationArguments args) {
    args.containsOption("my-flag-option"); // test if --my-flag-option was set
    args.getOptionValues("my-option");     // returns values of --my-option=value1 --my-option=value2 
    args.getOptionNames();                 // returns a list of all available options
    // do something with your args
  }
}

您的 run上下文成功启动后,将调用方法。

如果启动应用程序上下文之前需要访问参数,则可以简单地手动解析应用程序参数:

@SpringBootApplication
public class Application implements ApplicationRunner {

  public static void main(String[] args) {
    ApplicationArguments arguments = new DefaultApplicationArguments(args);
    // do whatever you like with your arguments
    // see above ...
    SpringApplication.run(Application.class, args);
  }

}

最后,如果您需要访问Bean中的参数,只需注入ApplicationArguments

@Component
public class MyBean {

   @Autowired
   private ApplicationArguments arguments;

   // ...
}

这太棒了 :)。
约翰·汉弗莱斯-w00te

3

我发现Argparse4j是最好的。它模仿Python的argparse库,该库非常方便且强大。


2

如果您想要轻量级的东西(罐子大小〜20 kb)并且易于使用,则可以尝试arguments-parser。它可以在大多数用例中使用,支持在参数中指定数组,并且不依赖于任何其他库。它适用于Java 1.5或更高版本。下面的摘录显示了有关如何使用它的示例:

public static void main(String[] args) {
    String usage = "--day|-d day --mon|-m month [--year|-y year][--dir|-ds directoriesToSearch]";
    ArgumentParser argParser = new ArgumentParser(usage, InputData.class);
    InputData inputData = (InputData) argParser.parse(args);
    showData(inputData);

    new StatsGenerator().generateStats(inputData);
}

可以在这里找到更多示例


1
链接已死。您杀死了您的项目吗?:-(
Beldaz '17

2

我不建议使用Apache Common CLI库,因为它是非线程安全的。

它使用带有静态变量和方法的有状态类来进行内部工作(例如OptionBuilder),并且仅应在单线程严格控制的情况下使用。


18
最好记住CLI库不是线程安全的。但是,我假设命令行解析通常是在应用程序启动期间在单个线程中完成的,然后根据参数,可以启动其他线程。
阿列克谢·伊凡诺夫

0

正如前面提到的评论之一(https://github.com/pcj/google-options)是一个不错的选择。

我想添加的一件事是:

1)如果遇到解析器反射错误,请尝试使用较新版本的番石榴。就我而言:

maven_jar(
    name = "com_google_guava_guava",
    artifact = "com.google.guava:guava:19.0",
    server = "maven2_server",
)

maven_jar(
    name = "com_github_pcj_google_options",
    artifact = "com.github.pcj:google-options:jar:1.0.0",
    server = "maven2_server",
)

maven_server(
    name = "maven2_server",
    url = "http://central.maven.org/maven2/",
)

2)运行命令行时:

bazel run path/to/your:project -- --var1 something --var2 something -v something

3)当需要使用帮助时,只需键入:

bazel run path/to/your:project -- --help

-1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.