这里会介绍各个模块相关类及方法
定义阶段
Option类
一个Option类就是一个命令行选项对象
属性 :
private final String option; // 短选项名称
private String longOption; // 长选项名称
private String argName; // 输入参数的一个提示信息,一般会是参数的类型,比如string,int,boolean等
private String description; // 描述信息
private boolean required; // 选项是否必须存在
private boolean optionalArg; // 参数值可选,比如可以是 -f filepath 或者直接 -f
private int argCount; // 设置接收参数的个数,有时候一个选项可能会接入多个参数值,一般都是一个
private Class<?> type; // 告诉接收参数的数据类型
private List<String> values; // 参数值列表,解析后的
private char valuesep; // values的分隔符
构造方法 :
// 短选项,长选项,是否含参数,选项描述
public Option(String option, String longOption, boolean hasArg, String description)
//短选项,描述 (longOption=null,hasArg=false)
public Option(String option, String description)
//短选项,是否含参数,选项描述 (longOption=null)
public Option(String option, boolean hasArg, String description)
// 通过Builder类来构造 如下 :
Option.builder()
.longOpt("global-config")
.hasArg()
.desc("Path of the global configuration file for Flink CDC pipelines")
.build();
方法 :
// 对比两个对象是否相等,内存地址或者对比longOption和option都一样的话返回true
public boolean equals(Object obj)
// 将选项复制,深拷贝
public Object clone()
// 获取opiton 如果为null,就返回longOption
String getKey()
// 其余public方法都是些set get方法
OptionGroup类
OptionGroup类可以将一组选项组织起来,确保用户只能选择其中的一个选项,一般是用在命令行中选项互斥的场景.
可以使用 addOption(String Option)
方法来添加group
Options类
Options类是存放Option类的容器
属性 :
// key 是短选项,value 是Option类
private final Map<String, Option> shortOpts = new LinkedHashMap();
// key 是长选项,value 是Option类
private final Map<String, Option> longOpts = new LinkedHashMap();
// 选项是否必须存在的列表
private final List<Object> requiredOpts = new ArrayList();
// Option组
private final Map<String, OptionGroup> optionGroups = new LinkedHashMap();
常用的方法 :
// 添加选项
public Options addOption(Option opt)
// 同Option类
public Options addOption(String opt, boolean hasArg, String description)
// 同Option类
public Options addOption(String opt, String description)
// 同Option类
public Options addOption(String opt, String longOpt, boolean hasArg, String description)
// 添加OptionGroup(一般用于互斥选项)
public Options addOptionGroup(OptionGroup group)
// 添加必选选项
public Options addRequiredOption(String opt, String longOpt, boolean hasArg, String description)
// 获取长选项中匹配的选项,相等的话就返回一个值,或者以opt开头的多个值
public List<String> getMatchingOptions(String opt)
// 根据选项名获取Option,优先取短选项,短的取不到就取长选项
public Option getOption(String opt)
// 判断是否包含这个长选项
public boolean hasLongOption(String opt)
// 判断是否包含短选项
public boolean hasShortOption(String opt)
// 判断是否包含短选项或者长选项
public boolean hasOption(String opt)
// 帮助信息list
List<Option> helpOptions()
解析阶段
CommandLineParser接口
CommandLIneParser 是一个解析接口,有多个实现类,一般用DefaultParser
实现类即可,其他的都弃用了,传入两个参数,一个是定义阶段构建的Options对象,还有一个就是main方法的String args[]数组,第三个参数是个布尔类型,如果为true,则无法识别的参数将停止解析,其余参数将添加到CommandLine的args列表中。如果为false,则无法识别的参数将触发ParseException。默认是false
public interface CommandLineParser {
CommandLine parse(Options options, String[] arguments) throws ParseException;
CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException;
}
DefaultParser实现类
这个类的作用就是根据Options 和 main方法传入的args数组解析成一个CommandLine对象,方便之后处理.
CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);
具体是如何解析的呢?
具体来看一个核心的解析方法即可
DefalultParser的parse方法
public CommandLine parse(final Options options, final String[] arguments, final Properties properties, final boolean stopAtNonOption)
throws ParseException {
this.options = options;
this.stopAtNonOption = stopAtNonOption;
skipParsing = false;
currentOption = null;
expectedOpts = new ArrayList<>(options.getRequiredOptions());
// clear the data from the groups
for (final OptionGroup group : options.getOptionGroups()) {
group.setSelected(null);
}
cmd = new CommandLine();
// 这个地方就是核心的解析方法,会将main方法的入参挨个遍历,然后转换成CommandLine对象
if (arguments != null) {
for (final String argument : arguments) {
handleToken(argument);
}
}
// check the arguments of the last option
checkRequiredArgs();
// add the default options
handleProperties(properties);
checkRequiredOptions();
return cmd;
}
核心处理方法 handleToken(String arg)
private void handleToken(final String token) throws ParseException {
currentToken = token;
if (skipParsing) {
cmd.addArg(token);
} else if ("--".equals(token)) {
skipParsing = true;
} else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) {
currentOption.addValueForProcessing(stripLeadingAndTrailingQuotesDefaultOn(token));
} else if (token.startsWith("--")) {
handleLongOption(token);
} else if (token.startsWith("-") && !"-".equals(token)) {
handleShortAndLongOption(token);
} else {
handleUnknownToken(token);
}
if (currentOption != null && !currentOption.acceptsArg()) {
currentOption = null;
}
}
大概的处理流程如下
比如我们用如下一个命令行
java Test -i /input/path -o /output/path
到java的args种会生成一个string数组,如下
["-i","/input/path","-o","ouput/path"]
parse方法的如下代码会遍历上面这个数组
if (arguments != null) {
for (final String argument : arguments) {
handleToken(argument);
}
}
如果遇到–或者-开头的字符串就会将其这个字符串的–或者-去掉,然后查询Options中是否有,有的话就添加到CommandLine的Options中,然后将currentOption记录成当前的Option,接着处理下一个字符串/input/path,这个字符串不是-或者–开头的,那么就会被currentOption调用addValueForProcessing方法,将其写入到Option的values属性中,以此类推
处理阶段
CommandLine类
通过DefaultParser类将Options和args转换成了一个CommondLine对象,这个对象就是可以方便的来处理参数了
属性 :
// 未识别的选项或者参数
private final List<String> args = new LinkedList<>();
// 识别到的选项及参数
private final List<Option> options = new ArrayList<>();
常用方法
// 根据Option获取它的参数值
public String getOptionValue(final Option option)
// 有的值是多个参数,获取参数列表
public String[] getOptionValues(final Option option)
// 是否包含该选项
public boolean hasOption(final Option opt)
HelpFormatter类
一般我们在用-h或者–help的选项的时候就可以打印帮助信息,这个类就是实现这一效果的
例子🌰
//实例代码 :
Options options = new Options();
options.addOption(OptionBuilder.withLongOpt("file").withDescription("The file to be processed").hasArg().withArgName("FILE").isRequired().create('f'));
options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version of the application").create('v'));
options.addOption(OptionBuilder.withLongOpt("help").create('h'));
String header = "Do something useful with an input file\n\n";
String footer = "\nPlease report issues at http://example.com/issues";
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("AntgCode", header, options, footer, true);
输出结果
执行结果如下 :
usage: AntgCode -f <FILE> [-h] [-v]
Do something useful with an input file
-f,--file <FILE> The file to be processed
-h,--help
-v,--version Print the version of the application
Please report issues at http://example.com/issues