超强命令行解析工具Apache Commons CLI

概述

为什么要写这篇文章呢?因为在读flink cdc3.0源码的时候发现了这个工具包,感觉很牛,之前写过shell命令,shell是用getopts来处理命令行参数的,但是其实写起来很麻烦,长时间不写已经完全忘记了,现在才发现原来java也有这种工具类,所以先学习一下这个的使用,也许之后自己在写一些小工具的时候就可以用到呢,在此也分享给大家.

简介

定义 : Apache Commons CLI 是一个java类库.( ps : 什么是定义,定义就是对某类事物的具象化,添加限定词,比如人是什么什么的动物,这里CLI应该再加上多个限定词,因为java类库的范围太大,传达给别人的信息就是不明确的 )

重新定义一下(以上纯抽风) : Apache Commons CLI 是一个可以非常方便解析入参 的,并且可以打印帮助信息的一个Java类 API库.

支持的选项类型 :

  • POSIX 选项, 例如 tar -zxvf foo.tar.gz
  • GNU 长选项, 例如 du --human-readable --max-depth=1
  • Java 选项, 例如 java -Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo
  • 带有值的短选项, 例如 gcc -O2 foo.c
  • 带有单个连字符的长选项, 例如 ant -projecthelp

命令行处理三阶段

命令行的处理分为三个阶段,定义阶段,解析阶段,处理阶段

注意 : 以下代码的版本为1.6.0,maven依赖如下

xml 复制代码
      <dependency>
          <groupId>commons-cli</groupId>
          <artifactId>commons-cli</artifactId>
          <version>1.6.0</version>
      </dependency>

1.定义阶段

创建命令行可以接收的选项,通过Options类来实现,它是Option的的容器.

java 复制代码
// 定义阶段
Options options = new Options();

Option filePathOption = new Option("f", true, "文件路径");
filePathOption.setRequired(true); // 文件路径为必传选项
Option daemonRunOption = new Option("d", false, "后台执行");
Option helpOption = new Option("h", "help", false, "帮忙文档");

options.addOption(filePathOption);
options.addOption(daemonRunOption);
options.addOption(helpOption);

参数说明 :

Option 的全参构造如下
Option(String option, boolean hasArg, String description)
Option(String option, String description)
Option(String option, String longOption, boolean hasArg, String description)

option : 短选项 例如 f 注意这里不要加-,命令会自动解析-后为短选项

longOption : 长选项 例如 file 注意这里不要加--,解析中--后会自动解析为长选项

hasArg : 是否需要参数值

description : 描述

Option的属性说明 :

java 复制代码
private final String option; // 短选项名称
private String longOption; // 长选项名称
private String argName; // 介绍较长,在下面
// 这个一般会在帮助信息中提示用户参数的相关信息,
// 例如在上面的 文件路径中加上了这个参数值"string"生成帮助信息的时候会生成这样
// -f <string>   文件路径
// 简单来说就是输入参数的一个提示信息,一般会是参数的类型,比如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的分隔符

2.解析阶段

解析阶段通过创建对象CommandLineParser,并且调用parse方法,传入options和args来解析输入的参数,将其转换成一个CommandLine对象,然后进行操作

demo如下

java 复制代码
// 定义阶段
Options options = new Options();
Option filePathOption = new Option("f", true, "文件路径");
filePathOption.setRequired(false); // 文件路径为必传选项
filePathOption.setArgName("string");
Option daemonRunOption = new Option("d", false, "后台执行");
Option helpOption = new Option("h", "help", false, "帮忙文档");
options.addOption(filePathOption);
options.addOption(daemonRunOption);
options.addOption(helpOption);
// 解析阶段
CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);

这里 new 了一个DefaultParser,其实还有其他的Parser,例如BasicPaser,GnuParser,PosixParser,但是这些都弃用了,所以就用DefaultParser.

3.查询阶段

查询阶段主要是根据入参的类型进行对应的操作

java 复制代码
// 查询阶段
if(args.length==0 || cmd.hasOption(helpOption)){
    //打印帮助信息(需要注意,如果某个参数是必选参数,在解析的时候就会报错,代码不会走到这里打印帮助信息)
    HelpFormatter formatter = new HelpFormatter();
    formatter.printHelp(" ",options);
}
// 获取文件路径
if(cmd.hasOption(filePathOption)){
    String filePath = cmd.getOptionValue(filePathOption);
    System.out.println("文件路径 : "+filePath);
}
// 后台运行
if(cmd.hasOption(daemonRunOption)){
    System.out.println("后台运行");
}

这里的判断如果再参数多的时候会写很长的if else,为了代码的健壮性可以使用策略模式(Strategy Pattern),将每个分支的处理逻辑对象封装成独立策略对象.

但是一般不会写很长的if else,通常会将接收到的参数转换成一个实体类然后进行之后的操作.

完整demo

java 复制代码
import org.apache.commons.cli.*;

public class TestCommonsCli {
    public static void main(String[] args) throws ParseException {
        // 定义阶段
        Options options = new Options();

        Option filePathOption = new Option("f", true, "文件路径");
        filePathOption.setRequired(false); // 文件路径为必传选项
        filePathOption.setArgName("string");
        Option daemonRunOption = new Option("d", false, "后台执行");
        Option helpOption = new Option("h", "help", false, "帮忙文档");

        options.addOption(filePathOption);
        options.addOption(daemonRunOption);
        options.addOption(helpOption);


        // 解析阶段
        CommandLineParser parser = new DefaultParser();
        CommandLine cmd = parser.parse(options, args);

        // 查询阶段
        if(args.length==0 || cmd.hasOption(helpOption)){
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp(" ",options);
        }

        // 获取文件路径
        if(cmd.hasOption(filePathOption)){
            String filePath = cmd.getOptionValue(filePathOption);
            System.out.println("文件路径 : "+filePath);
        }

        // 后台运行
        if(cmd.hasOption(daemonRunOption)){
            System.out.println("后台运行");
        }
        
    }
}

帮助文档运行结果

代码地址 : https://github.com/codeAntg/Antgeek/tree/main/learning/apache/common/cli

参考

https://commons.apache.org/proper/commons-cli/

https://blog.csdn.net/SunnyYoona/article/details/128438785

https://iowiki.com/commons_cli/commons_cli_overview.html

https://blog.csdn.net/weixin_42116348/article/details/135189025