关于命令行参数解析的库还挺多,调研之后选择了 picocli 这个库
因为其文档很全,所以下面是一个快速使用的教程,抓住主干再揪细节。
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.5.2</version>
</dependency>
效果
先看看,再实现
$ java -jar cli.jar -h
Usage: java -jar cli.jar [-hV] -p=<secret> -u=<user> [-c=<String=String>]...
-s=<servers>[,<servers>...] [-s=<servers>[,
<servers>...]]...
-c, --config=<String=String>
-c k1=v1 -c k2=v2
-h, --help Show this help message and exit.
-p, --password=<secret> login secret or password
-s, --server=<servers>[,<servers>...]
servers,split by comma: host1:port1,host2:
port2
-u, --user=<user> login username
-V, --version Print version information and exit.
定义参数类
使用注解来定义参数类
一般只需要2个注解即可搞定:
@CommandLine.Command
: 定义在类上,声明应用执行名字,版本等@CommandLine.Option
: 用在属性上,代表参数
下面是一个示例:
import picocli.CommandLine;
import java.util.Map;
@CommandLine.Command(name = "java -jar cli.jar")
public class CliParam {
@CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = " cmd cli help msg")
public boolean help = false;
@CommandLine.Option(names = {"-s", "--server"}, required = true, split = ",", converter = ServerTypeConverter.class,
description = " servers,split by comma: host1:port1,host2:port2 ")
public String[] servers;
@CommandLine.Option(names = {"-u", "--user"}, required = true, description = "login username")
public String user;
@CommandLine.Option(names = {"-p", "--password"}, required = true,
description = "login secret or password")
public String secret;
/**
* 一些额外的参数
*/
@CommandLine.Option(names = {"-c", "--config"}, description = " -c k1=v1 -c k2=v2")
public Map<String, String> configs;
static class ServerTypeConverter implements CommandLine.ITypeConverter<String> {
@Override
public String convert(String s) {
final String[] hp = s.split(":");
if (hp.length != 2) {
throw new CommandLine.TypeConversionException("Invalid servers, must be: host1:port1,host2:port2," +
" but was:'" + s + "'");
}
String host = hp[0].trim();
int port = Integer.parseInt(hp[1].trim());
return host + (port == 80 ? "" : ":" + port);
}
}
}
其中ServerTypeConverter
可以自定义解析,这里接收一个逗号分割的字符串,返回一个数组。
声明版本
可以直接写死,也可以动态加载
@CommandLine.Command(name = "java -jar cli.jar",
versionProvider = CliParam.MyVersionProvider.class)
public class CliParam {
/**
* 动态获取版本号,从内部配置里获取
*/
static class MyVersionProvider implements CommandLine.IVersionProvider {
@Override
public String[] getVersion() throws Exception {
final InputStream in = MyVersionProvider.class.getClassLoader().getResourceAsStream("config.properties");
final Properties p = new Properties();
p.load(in);
return new String[]{"My APP", "Version " + p.getProperty("version", "unknown")};
}
}
}
进阶
我们知道每个命令行都会有 -h
和-v
这俩选项,所以人家早就考虑好了。
只需要一个选项即可:
@CommandLine.Command(name = "java -jar cli.jar",
mixinStandardHelpOptions = true,
versionProvider = CliParam.MyVersionProvider.class)
public class CliParam {}
使用参数解析
定义完了,就需要在main方法里解析了。
public static void main(String[] args) throws Exception {
// 解析参数
final CliParam CliParam = new CliParam();
final CommandLine commandLine = new CommandLine(CliParam);
try {
final CommandLine.ParseResult parseResult = commandLine.parseArgs(args);
if (parseResult.isUsageHelpRequested()) {
commandLine.usage(System.out);
System.exit(0);
}
if (parseResult.isVersionHelpRequested()) {
commandLine.printVersionHelp(System.out);
System.exit(0);
}
CliParam.setINSTANCE(CliParam);
} catch (CommandLine.ParameterException e) {
commandLine.usage(System.out);
System.exit(1);
}
// 使用 cliParam
当遇到未知参数时,会抛出 CommandLine.ParameterException
, 捕获了打印help即可。
正常解析完如果是 -V,也需要打印版本号然后退出。
更多强大功能,请参考文档。