命令行开发
一、Java 命令行开发方案
什么是命令行程序?
命令行程序俗称 CLI(Command Line Interface),是指通过命令行界面运行的应用程序。通过终端窗口接收用户输入的 纯文本 命令,并执行相应的任务。
一些常见的命令行环境包括 Unix/Linux 的终端、Windows 的命令提示符和 PowerShell 等。学编程的同学可能没有开发过命令行程序,但一定都接触过终端!
命令的结构
输入给命令行的命令通常包含:
- command:命令类型,具体要做的事
- option:选项,用于改变命令的行为
- parameter:参数,传递给命令行工具的值
为什么要开发命令行?
命令行程序的几个优点:
- 不依赖于特定的图形界面,非常轻量
- 通常可以直接在操作系统自带的终端环境中运行
- 可以和用户交互、给用户输入引导和帮助手册
- 内置一些快捷操作(比如查看历史命令、上下切换命令)
还有一个最大的优点 —— 简单直接,比如复制粘贴别人写好的命令就能执行,而不用像使用网页一样点击多次,非常符合程序员的使用(偷懒)习惯,less is more!
命令行的作用
回归到我们的项目,命令行的作用是什么呢?
可以使用命令行程序来和用户交互,引导用户输入代码生成的定制参数,并将输入参数封装为配置对象,然后 “喂” 给之前编写好的代码生成器来生成文件。
比如之间我们的动态模板配置 MainTemplateConfig 中包含 loop、author、outputText 这 3 个参数,那么可以让用户输入下列完整命令,来给模板配置传值:
generate --loop --author yupi --outputText good
或者更人性化一些,允许用户交互式输入,比如先输入 generate,然后按照系统的提示依次输入其他参数,如下图:
实现方案
了解了命令行的作用后,我们如何用 Java 语言开发命令行程序呢?
自主实现
最直接的方案就是使用 Java 内置的类库自主实现,比如通过 Scanner 读取用户的输入,再配合 while、if … else 等流程控制语句实现多次输入和提示。
示例代码如下:
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
// 读取整数
int number = scanner.nextInt();
}
这种方式虽然简单,但缺点也很多:
1)需要自主解析用户的输入
前面已经介绍了命令的结构,可以看到一句命令可能非常复杂,包含各种选项和参数,而且用户还可能乱序输入不同的参数。
如何从这么一句复杂的命令中提取出需要的值,是一个难题。
2)需要自主编写一套获取参数的交互流程
比如用户输入了异常值怎么办?是中断程序还是给出提示?给出什么提示?这些都要开发者自己考虑。
3)需要自主实现更多高级功能
比如基本所有命令行工具都有的帮助手册命令(–help)、颜色高亮等。
如果这些和业务无关的功能如果都需要自己开发,那真的是太浪费时间了!
所以建议直接用现成的第三方命令行开发库。
第三方库
这里鱼皮做了充分的调研,收集了几种经典的 Java 命令行开发相关库,简单分为 3 类:
1)命令行工具开发框架
专门用于开发命令行工具的框架
⭐️ Picocli(https://github.com/remkop/picocli):优点是 GitHub 的 star 数多(4k+)、持续更新,支持颜色高亮、自动补全、子命令、帮助手册等,最推荐。
2)控制台输入处理库
能够对用户在控制台的输入进行处理的库
JLine(https://github.com/jline/jline3):支持自动补全、行编辑、查看命令历史等,但官方文档内容略少、学习成本高。参考教程:https://zhuanlan.zhihu.com/p/43835406
3)命令行解析库
支持对命令行进行解析取值的库
⭐️ JCommander(https://github.com/cbeust/jcommander):注解驱动,可以直接把命令映射到对象上,从而大幅简化代码。GitHub 上近 2k star,比较推荐。
Apache Commons CLI(https://github.com/apache/commons-cli):简单易用,但是功能不够多。参考教程:https://blog.csdn.net/liuxiangke0210/article/details/78141887
Jopt Simple(https://github.com/jopt-simple/jopt-simple):不推荐。冷门、很久没维护、star 数少、生态不好。
综上,最推荐的是专业的命令行开发框架 Picocli,下面从 0 开始带大家入门和实战这个框架。
二、Picocli 命令行框架入门
网上有关 Picocli 框架的教程非常少,最推荐的入门方式除了看鱼皮的教程外,就是阅读官方文档了。
官方文档:https://picocli.info/
推荐从官方提供的快速入门教程开始:https://picocli.info/quick-guide.html
一般我们学习新技术的步骤是:先跑通入门 Demo,再学习该技术的用法和特性。
入门 Demo
1)在 yuzi-generator-basic 项目的 pom.xml 文件中引入 picocli 的依赖:
<!-- https://picocli.info -->
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.5</version>
</dependency>
然后我们在 com.yupi 包下新建 cli.example 包,用于存放所有和 Picocli 入门有关的示例代码。
2)复制官方快速入门教程中的示例代码到 com.yupi.cli.example 包下,并略微修改 run 方法中的代码,打印参数的值。
完整代码如下:
package com.yupi.cli.example;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@Command(name = "ASCIIArt", version = "ASCIIArt 1.0", mixinStandardHelpOptions = true)
public class ASCIIArt implements Runnable {
@Option(names = { "-s", "--font-size" }, description = "Font size")
int fontSize = 19;
@Parameters(paramLabel = "<word>", defaultValue = "Hello, picocli",
description = "Words to be translated into ASCII art.")
private String[] words = { "Hello,", "picocli" };
@Override
public void run() {
// 自己实现业务逻辑
System.out.println("fontSize = " + fontSize);
System.out.println("words = " + String.join(",", words));
}
public static void main(String[] args) {
int exitCode = new CommandLine(new ASCIIArt()).execute(args);
System.exit(exitCode);
}
}
看不懂这段代码没关系,官方文档已经给了非常详细的解释:
帮大家翻译一下:
- 创建一个实现
Runnable或Callable接口的类,这就是一个命令。 - 使用
@Command注解标记该类并为其命名,mixinStandardHelpOptions属性设置为 true 可以给应用程序自动添加--help和--version选项。 - 通过
@Option注解将字段设置为命令行选项,可以给选项设置名称和描述。 - 通过
@Parameters注解将字段设置为命令行参数,可以指定默认值、描述等信息。 - Picocli 会将命令行参数转换为强类型值,并自动注入到注解字段中。
- 在类的
run或call方法中定义业务逻辑,当命令解析成功(用户敲了回车)后被调用。 - 在
main方法中,通过CommandLine对象的execute方法来处理用户输入的命令,剩下的就交给 Picocli 框架来解析命令并执行业务逻辑啦~ CommandLine.execute方法返回一个退出代码。可以调用System.exit并将该退出代码作为参数,从而向调用进程表示成功或失败。
3)让我们更改主程序的执行参数(args)来测试程序,能够成功看到输出结果,如下图:
通过这个入门 Demo,我们可以简单总结一个命令的开发流程:
- 创建命令
- 设置选项和参数
- 编写命令执行的业务逻辑
- 通过 CommandLine 对象接受输入并执行命令
在跑通了入门 Demo 后,我们来学习一些 Picocli 开发命令行的实用功能。
实用功能
帮助手册
通过给类添加的 @Command 注解参数 mixinStandardHelpOptions 设置为 true 来开启:
@Command(name = "ASCIIArt", mixinStandardHelpOptions = true)
然后将主程序的输入参数设置为 --help 就能打印出命令的帮助手册信息了,如下图:
可以看到,Picocli 生成的帮助手册不仅规范、而且清晰完整。
命令解析
Picocli 最核心的能力就是命令解析,能够从一句完整的命令中解析选项和参数,并填充到对象的属性中。
Picocli 使用注解的方式实现命令解析,不需要自己编写代码,整个类看起来非常清晰。
最核心的 2 个注解其实在入门 Demo 中我们已经使用到了:
@Option注解用于解析选项@Parameters注解用于解析参数
示例代码如下:
@Option(names = { "-s", "--font-size" }, description = "Font size")
int fontSize = 19;
@Parameters(paramLabel = "<word>", defaultValue = "Hello, picocli",
description = "Words to be translated into ASCII art.")
private String[] words = { "Hello,", "picocli" };
可以给这些注解指定参数,比较常用的参数有:
1)@Option 注解的 names 参数:指定选项英文名称。
2)description 参数:指定描述信息,从而让生成的帮助手册和提示信息更清晰。
3)@Parameters 注解的 paramLabel 参数:参数标签,作用类似于描述信息。
4)@Parameters 注解的 defaultValue 参数:默认值,参考文档:https://picocli.info/#_default_values
5)required 参数:要求必填,参考文档:https://picocli.info/#_required_arguments
示例代码如下:
class RequiredOption {
@Option(names = "-a", required = true)
String author;
}
此外,命令解析天然支持 多值选项 ,只需要把对象属性的类型设置为数组类型即可,比如:
@Option(names = "-option")
int[] values;
具体可以参考官方文档:https://picocli.info/#_multiple_values
更多关于选项和参数注解的用法,也可以阅读官方文档学习:https://picocli.info/quick-guide.html#_options_and_parameters
交互式输入
所谓的交互式输入就是允许用户像跟程序聊天一样,在程序的指引下一个参数一个参数地输入。
如下图:
Picocli 为交互式输入提供了很好的支持,我梳理了大概 4 种交互式输入的模式。
1)基本能力
交互式输入的一个典型应用场景就是:用户要登录时,引导 ta 输入密码。
官方已经为我们提供了一段交互式输入的示例代码,鱼皮对它进行了简化,示例代码如下:
参考官方文档:https://picocli.info/#_interactive_password_options
package com.yupi.cli.example;
import picocli.CommandLine;
import picocli.CommandLine.Option;
import java.util.concurrent.Callable;
public class Login implements Callable<Integer> {
@Option(names = {"-u", "--user"}, description = "User name")
String user;
@Option(names = {"-p", "--password"}, description = "Passphrase", interactive = true)
String password;
public Integer call() throws Exception {
System.out.println("password = " + password);
return 0;
}
public static void main(String[] args) {
new CommandLine(new Login()).execute("-u", "user123", "-p");
}
}
让我们分析下上面的代码,主要包含 4 个部分:
1)首先命令类需要实现 Callable 接口
public class Login implements Callable<Integer> {
...
}
2)将 @Option 注解的 interactive 参数设置为 true,表示该选项支持交互式输入
@Option(names = {"-p", "--password"}, interactive = true)
String password;
3)在所有参数都输入完成后,会执行 call 方法,可以在该方法中编写具体的业务逻辑:
public Integer call() throws Exception {
System.out.println("password = " + password);
return 0;
}
4)在 Main 方法中执行命令并传入参数:
java
new CommandLine(new Login()).execute("-u", "user123", "-p");
执行上述代码,看到程序提示我们输入密码:
注意,如果以 jar 包方式运行上述程序,用户的输入默认是不会显示在控制台的(类似输入密码时的体验)。从 Picocli 4.6 版本开始,可以通过指定 @Option 注解的 echo 参数为 true 来显示用户的输入,并通过 prompt 参数指定引导用户输入的提示语。
2)多个选项交互式
Picocli 支持在一个命令中指定多个交互式输入的选项,会按照顺序提示用户并接收输入。
在上述代码中再增加一个 checkPassword 选项,同样开启交互式输入,代码如下:
public class Login implements Callable<Integer> {
@Option(names = {"-u", "--user"}, description = "User name")
String user;
@Option(names = {"-p", "--password"}, description = "Passphrase", interactive = true)
String password;
@Option(names = {"-cp", "--checkPassword"}, description = "Check Password", interactive = true)
String checkPassword;
public Integer call() throws Exception {
System.out.println("password = " + password);
System.out.println("checkPassword = " + checkPassword);
return 0;
}
public static void main(String[] args) {
new CommandLine(new Login()).execute("-u", "user123", "-p");
}
}
但运行上述代码我们会发现,怎么只提示我输入了密码,没提示我输入确认密码呢?
这是由于 Picocli 框架的规则,用户必须在命令中指定需要交互式输入的选项(比如 -p),才会引导用户输入。
所以我们需要修改上述代码中的 main 方法,给命令输入补充 -cp 参数:
public static void main(String[] args) {
new CommandLine(new Login()).execute("-u", "user123", "-p", "-cp");
}
再次执行,这下程序会依次提醒我们输入两个选项啦:
根据实际使用情况,又可以将交互式输入分为 2 种模式:
- 可选交互式:用户可以直接在整行命令中输入选项,而不用给用户提示信息。
- 强制交互式:用户必须获得提示并输入某个选项,不允许不填写。
下面分别讲解这两种模式。
最后更新于 2026-02-18 18:15