您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

怎么基于Java编写一个CLI工具?

时间:2023-12-11 13:00:30  来源:微信公众号  作者:Java技术指北

CLI

CLI,全称为命令行界面(Command Line Interface),是一种用于通过键盘输入指令与操作系统进行交互的软件机制。这种界面是在图形用户界面得到普及之前使用最为广泛的用户界面,并且, 即使在当前图形用户界面广泛使用的环境下,CLI仍然有其独特的优势和广泛的应用。

对于CLI,它的一个重要特性就是效率。用户可以在一条文本命令中对多个文件执行操作,而不需要在各个窗口之间切换,节省了大量时间。此外,如果你已经熟悉了这些命令,那么你可以非常快速地浏览系统并与之交互。

构建CLI的工具很多,今天主要基于JAVA语言来实现,其中Apache Commons CLI框架提供了这样的便利。今天结合之前学习的graalVM提供的native-image工具,来生成一个exe类型的可执行文件,由于graalVM的跨平台性,我们还能生成各个平台的CLI命令来辅助完成更多的工作。

Apache Commons CLI是一个用于编写命令行界面的Java库。它提供了一个灵活的框架,可以很容易地定义和解析命令行参数。这个库的主要优点是它可以处理各种类型的参数,包括选项、位置参数、可选参数等。

构成

下面以native-image为例,通过在终端输入native-image --help可以看到以下信息

_> native-image --help

GraalVM Native Image (https://www.graalvm.org/native-image/)

This tool can ahead-of-time compile Java code to native executables.

Usage: native-image [options] class [imagename] [options]
           (to build an image for a class)
   or  native-image [options] -jar jarfile [imagename] [options]
           (to build an image for a jar file)
   or  native-image [options] -m <module>[/<mAInclass>] [options]
       native-image [options] --module <module>[/<mainclass>] [options]
           (to build an image for a module)

where options include:

    @argument files       one or more argument files containing options
    -cp <class search path of directories and zip/jar files>
    -classpath <class search path of directories and zip/jar files>
    --class-path <class search path of directories and zip/jar files>
                          A ; separated list of directories, JAR archives,
                          and ZIP archives to search for class files.
    -p <module path>
    --module-path <module path>...
                          A ; separated list of directories, each directory
                          is a directory of modules.

一个合格的CLI基本都会提供help选项来展示,这个CLI的语法、选项以及功能描述。从上面的输出可以看到help主要包括:

  1. 介绍:主要对命令的功能的描述,包括官网、版本以及一些内在系数等
  2. 用法:包括命令语法格式、配置项、参数等信息
  3. 参数说明:具体配置项参数的说明,以及具体的功能描述

Common-CLI

  • 定义阶段:在Java代码中定义Option参数,定义参数、是否需要输入值、简单的描述等
  • 解析阶段:应用程序传入参数后,CLI进行解析
  • 询问阶段:通过查询CommandLine询问进入到哪个程序分支中

定义阶段

主要是借助Option类提供的API来构建各种选项以及参数信息,下面是对应API的描述:

返回值

方法名

说明

Options

addOption(Option opt)

添加一个选项实例

Options

addOption(String opt, boolean hasArg, String description)

添加一个只包含短名称的选项

Options

addOption(String opt, String description)

添加一个只包含短名称的选项

Options

addOption(String opt, String longOpt, boolean hasArg, String description)

添加一个包含短名称和长名称的选项

Options

addOptionGroup(OptionGroup group)

添加一个选项组

List

getMatchingOptions(String opt)

获得匹配选项的长名称集合

Option

getOption(String opt)

通过长名称或短名称获得选项

OptionGroup

getOptionGroup(Option opt)

获得选项所在的选项组

Collection

getOptions()

获得一个只读的选项集合

List

getRequiredOptions()

获得必须的选项集合

boolean

hasLongOption(String opt)

判断是否存在选项

boolean

hasOption(String opt)

判断是否存在选项

boolean

hasShortOption(String opt)

判断是否存在选项

解析阶段

主要对输入参数的解析,也就是main方法的参数,默认提供下面3中语法解析的支持:

  • DefaultParser:提供了基础的解析功能,能解析简单的命令行参数。(比如:java -Djava.awt.headless=true -Djava.NET.useSystemProxies=true Foo)
  • PosixParser:提供了解析POSIX形式参数的功能。(比如:tar -zxvf foo.tar.gz)
  • GnuParser:提供了解析长参数及Java命令中参数的功能。(比如:du --human-readable --max-depth=1)

询问阶段

基于上一步解析后,会将参数解析成CommandLine,结合Option中的配置,需要我们完成各种配置、参数匹配后的业务处理流程,类型下面这样:

if( commandLine.hasOption("help") ){
          helper.printHelp("calendar [options] nnwhere options include:", null, options, null, false);
          return;
      }

      if( commandLine.hasOption("version") ){
          printResult("1.0.0");
          return;
      }

解析的过程有时候会比较些复杂,示例中是针对单一选项的分支,当多个选项混合使用时,比如tar -zxvf xxx.tar.gz这样的,当然前提是我们定义的CLI支持这种风格。

示例

下面通过一个简单的示例看下如何构建一个CLI的工具:该示例的作用是按指定格式输出当前日期:

clendar -o yyyy-MM-dd
  • 定义配置项
 
private static Options initOptions() {
        Options options = new Options();

        options.addOption(Option.builder("H")
                .longOpt("help")
                .desc("show help information").build());

        options.addOption(Option.builder("V")
                .longOpt("version")
                .desc("show help information").build());

        options.addOption(Option.builder("O")
                .longOpt("out")
                .hasArg(true)
                .argName("fmt") // 只是定义
                .required(false)
                .desc("configure the date output format").build());

        return options;
    }
  • 解析参数
 
private static CommandLine parseArguments(Options options, String[] args){
        CommandLineParser parser = new DefaultParser();

        try {
            return parser.parse(options, args);
        } catch (ParseException e) {
            System.err.println(e.getMessage());
        }
        return null;
    }
  • 询问阶段
private static void handleCommand(Options options, CommandLine commandLine) {
        if(ArrayUtils.isEmpty(commandLine.getOptions()) ){
            printResult("Please specify options for calendar building or use --help for more info.");
            return;
        }

        if( commandLine.hasOption("help") ){
            helper.printHelp("calendar [options] nnwhere options include:", null, options, null, false);
            return;
        }

        if( commandLine.hasOption("version") ){
            printResult("1.0.0");
            return;
        }

        if( commandLine.hasOption("out") ){
            String fmt = commandLine.getOptionValue("out");
            if(StringUtils.isEmpty(fmt)){
                fmt = "yyyy-MM-dd HH:mm:ss";
            }
            printResult(DateFormatUtils.format(new Date(), fmt));
            return;
        }

        // calendar: 'x' is not a git command. See 'calendar --help'.
        helper.printHelp(String.format("calendar: '%s' is not a calendar command. See 'calendar --help'.", Arrays.toString(commandLine.getArgs())), options, false);
    }

定义程序入口:

public static void main(String[] args) {
        // 定义阶段
        Options options = initOptions();
        // 解析阶段
        CommandLine commandLine = parseArguments(options, args);
        // 询问阶段
        handleCommand(options, commandLine);
    }

打包

这里我们引入maven-assembly-plugin插件,主要帮助在打包时将依赖包一并写入jar文件,同时将入口文件定义到manifest:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.3.0</version>
    <executions>
        <execution>
            <id>package-jar-with-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>${main-class}</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <!-- bin,jar-with-dependencies,src,project -->
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </execution>
    </executions>
</plugin>

可以直接通过maven插件或者下的命令,将上面的代码打包成jar文件

mvn clean package

测试jar

如果安装上面的配置,最终会在项目target目录输出一个以jar-with-dependencies为后缀的jar文件,通过下面的命令可以测试cli命令

java -jar ./target/calendar-jar-with-dependencies.jar -h

这样的CLI可不是我们想要的,一来需要依赖JRE的运行环境,同时调用极其不方便。

生成exe

如果你看过之前的文章,关于GraalVM的使用,按照文档下载并配置好运行环境后,可以通过下面的命令对上一步的jar文件进一步处理

native-image -jar [jar] -o [name]

native-image -jar ./target/calendar-jar-with-dependencies.jar -o calendar

通过上面的命令会生成一个calendar.exe文件,这样将其加入到环境变量后,则可以在windows平台终端上使用了

对于不喜欢直接使用命令的,当然这里也可以使用插件exec-maven-plugin,在maven生命周期package阶段,自动执行上面的命令,这样整个过程只需要执行mvn clean package即可

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <id>native-image-App</id>
            <phase>package</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <environmentVariables>
                </environmentVariables>
                <!-- native-image -jar ./target/tool-jar-with-dependencies.jar -o tool -->
                <executable>native-image</executable>
                <arguments>
                    <argument>-jar</argument>
                    <argument>${project.basedir}/target/${project.build.finalName}-jar-with-dependencies.jar</argument>
                    <argument>-o</argument>
                    <argument>${project.build.finalName}</argument>
                </arguments>
            </configuration>
        </execution>
    </executions>
</plugin>

测试exe

在终端执行下面的命令接口看到预期的结果:

calendar.exe -O yyyy-MM-dd

总结

总的来说,Apache Commons CLI是一个非常强大的工具,可以帮助你轻松地处理命令行参数。无论你的应用程序需要处理多少个参数,或者这些参数的类型是什么, Commons CLI都可以提供帮助。



Tags:Java   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  Search: Java  点击:(4)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  Search: Java  点击:(25)  评论:(0)  加入收藏
Oracle正式发布Java 22
Oracle 正式发布 Java 22,这是备受欢迎的编程语言和开发平台推出的全新版本。Java 22 (Oracle JDK 22) 在性能、稳定性和安全性方面进行了数千种改进,包括对Java 语言、其API...【详细内容】
2024-03-21  Search: Java  点击:(10)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  Search: Java  点击:(20)  评论:(0)  加入收藏
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  Search: Java  点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20  Search: Java  点击:(19)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18  Search: Java  点击:(24)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  Search: Java  点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  Search: Java  点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06  Search: Java  点击:(52)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(19)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(24)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(55)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(68)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(72)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(88)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(105)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(95)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(74)  评论:(0)  加入收藏
站内最新
站内热门
站内头条