picocli-入门
时间:2022-10-28 23:30:00
https://picocli.info/#_introduction
前言
相信每个Java程序员都用过Scanner
,兴奋地写了一个命令行程序。
但是,使用命令行程序也很实用Java编写一个强大的命令程序并不容易,主要有以下痛点:
- 没有成熟的框架
包装参数接收、参数提示、参数验证
- 很难处理
参数的互斥
以及特定命令的相互依赖 - 无法进行
命令自动补全
- 由于JVM解释执行字节码,并解释执行字节码JIT不能在短期执行中发挥作用,Java慢慢启动命令行程序
- 集成SpringBoot以及其他组件,
启动更慢
这些问题都可以使用Picocli来解决
引用:https://blog.csdn.net/qq_40419564/article/details/115290878
Picocli 基本介绍
Every main method deserves picocli!
Picocli aims to be the easiest way to create rich command line applications that can run on and off the JVM.
入门
引入maven依赖
<dependency> <groupId>info.picocligroupId> <artifactId>picocliartifactId> <version>4.6.3version> dependency>
应用demo
@CommandLine.Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0", description = "Prints the checksum (SHA-256 by default) of a file to STDOUT.") public class CheckSum implements Callable<Integer> {
@CommandLine.Parameters(index = "0", description = "The file whose checksum to calculate.") private File file; @CommandLine.Option(names = {
"-a", "--algorithm"}, description = "MD5, SHA-1, SHA-256, ...") prvate String algorithm = "SHA-256";
@Override
public Integer call() throws Exception {
// your business logic goes here...
byte[] fileContents = Files.readAllBytes(file.toPath());
byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1, digest));
return 0;
}
public static void main(String... args) {
int exitCode = new CommandLine(new CheckSum()).execute(args);
System.exit(exitCode);
}
}
maven打包
<build>
<finalName>demo1finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
<archive>
<manifest>
<mainClass>cn.jhs.CheckSummainClass>
manifest>
archive>
configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
执行命令mvn clean package
Command
> java -jar target/demo1-jar-with-dependencies.jar
Missing required parameter: ''
Usage: checksum [-hV] [-a=<algorithm>] <file>
Prints the checksum (SHA-256 by default) of a file to STDOUT.
<file> The file whose checksum to calculate.
-a, --algorithm=<algorithm>
MD5, SHA-1, SHA-256, ...
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
> echo "hello" > hello.txt
> java -jar target/demo1-jar-with-dependencies.jar hello.txt
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
使用别名
> alias checksum=java\ -jar\ target/demo1-jar-with-dependencies.jar
> checksum hello.txt -a SHA-1
f572d396fae9206628714fb2ce00f72e94f2258f
Options and Parameters
Command line arguments可以分为options
和positional parameters
。
- option
有一个名称
positional parameters
位置参数通常是option
后面的值,但它们可能是混合的。
Options
option
必须有一个或多个名称
。 Picocli 允许您使用任何您想要的选项名称。
默认情况下,
option
名称区分大小写.
例
class Tar {
@Option(names = "-c", description = "create a new archive")
boolean create;
@Option(names = {
"-f", "--file" }, paramLabel = "ARCHIVE", description = "the archive file")
File archive;
@Parameters(paramLabel = "FILE", description = "one or more files to archive")
File[] files;
@Option(names = {
"-h", "--help" }, usageHelp = true, description = "display a help message")
private boolean helpRequested = false;
}
TestCase
String[] args = {
"-c", "--file", "result.tar", "file1.txt", "file2.txt" };
Tar tar = new Tar();
new CommandLine(tar).parseArgs(args);
assert !tar.helpRequested;
assert tar.create;
assert tar.archive.equals(new File("result.tar"));
assert Arrays.equals(tar.files, new File[] {
new File("file1.txt"), new File("file2.txt")});
Interactive (Password) Options
对于标记为Interactive Options
和positional parameters
,会提示用户在控制台上输入一个值。
交互式
例
class Login implements Callable<Integer> {
@Option(names = {
"-u", "--user"}, description = "User name")
String user;
//响应式 Option
@Option(names = {
"-p", "--password"}, description = "Passphrase", interactive = true)
char[] password;
public Integer call() throws Exception {
byte[] bytes = new byte[password.length];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) password[i]; }
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes);
System.out.printf("Hi %s, your password is hashed to %s.%n", user, base64(md.digest()));
// null out the arrays when done
Arrays.fill(bytes, (byte) 0);
Arrays.fill(password, ' ');
return 0;
}
private String base64(byte[] arr) {
/* ... */ }
}
testcase
执行命令:new CommandLine(new Login()).execute("-u", "user123", "-p");
然后会提示用户输入一个值:
Enter value for --password (Passphrase):
在 Java 6 或更高版本上运行时,用户输入不会回显到控制台。
用户输入密码值并按下回车后,将call()调用该方法,该方法将打印如下内容:
Hi user123, your passphrase is hashed to 75K3eLr+dx6JJFuJ7LwIpEpOFmwGZZkRiB84PURz6U8=.
#### 可选的 Interactive
默认情况下,` Interactive Options`会导致应用程序等待标准输入上的输入。
对于即需要以交互方式 又需要以批处理模式运行的命令,如果该选项**可以选择的**,这将很有用。
**arity**
```java
@Option(names = "--user")
String user;
@Option(names = "--password", arity = "0..1", interactive = true)
char[] password;
- 通过以下输入,密码字段将被初始化为“123”,而不提示用户输入:
--password 123 --user Joe
- 但是,如果未指定密码,
--password --user Joe
, 则会提示用户输入密码。
Short (POSIX) Options
class ClusteredShortOptions {
@Option(names = "-a") boolean aaa;
@Option(names = "-b") boolean bbb;
@Option(names = "-c") boolean ccc;
@Option(names = "-f") String file;
}
以下命令行参数都是等效的,解析它们会得到相同的结果:
<command> -abcfInputFile.txt
<command> -abcf=InputFile.txt
<command> -abc -f=InputFile.txt
<command> -ab -cf=InputFile.txt
<command> -a -b -c -fInputFile.txt
<command> -a -b -c -f InputFile.txt
<command> -a -b -c -f=InputFile.txt
...
Boolean Options
Boolean Options
通常不需要参数:在命令行中指定选项名称就足够了。
class BooleanOptions {
@Option(names = "-x") boolean x;
}
- x 的值默认为 false,
- 如果在命令行上指定了
-x
,则设置为 true(与默认值相反)。 - 如果在命令行上多次指定
-x
,则 x 的值保持为true
Negatable Options-否定选项
@Command(name = "negatable-options-demo")
class NegatableOptionsDemo {
@Option(names = "--verbose", negatable = true) boolean verbose;
@Option(names = "-XX:+PrintGCDetails", negatable = true) boolean printGCDetails;
@Option(names = "-XX:-UseG1GC", negatable = true) boolean useG1GC = true;
}
上述示例的使用帮助如下所示:
Usage: negatable-options-demo
[--[no-]verbose]
[-XX:(+|-)PrintGCDetails]
[-XX:(+|-)UseG1GC]
--[no-]verbose Show verbose output
-XX:(+|-)PrintGCDetails Prints GC details
-XX:(+|-)UseG1GC Use G1 algorithm for GC
Positional Parameters
Explicit Index - 显式索引
使用[0,+oo)
索引属性来准确指定要捕获的参数。数组或集合字段可以捕获多个值。
例
class PositionalParameters {
@Parameters(index = "0") InetAddress host;
@Parameters(index = "1") int port;
@Parameters(index = "2..*") File[] files;
@Parameters(hidden = true) // "hidden": don't show this parameter in usage help message
List<String> allParameters; // no "index" attribute: captures _all_ arguments
}
testcase
String[] args = {
"localhost", "12345", "file1.txt", "file2.txt" };
PositionalParameters params = CommandLine.populateCommand(new PositionalParameters(), args);
assert params.host.getHostName().equals("localhost");
assert params.port == 12345;
assert Arrays.equals(params.files, new File[] {
new File("file1.txt"), new File("file2.txt")});
assert params.allParameters.equals(Arrays.asList(args));
Omitting the Index -省略索引
可以省略 index 属性。
- 对于多值位置参数(数组或集合),省略 index 属性意味着该字段捕获所有位置参数(相当于
index = "0..*"
)。 - 对于单值位置参数,
- 在 picocli 4.3 之前,单值位置参数的默认索引也是
index = "0..*"
,即使只有一个值(通常是第一个参数) 可以被捕获。 - 从 4.3 版开始,picocli 根据同一命令中定义的其他位置参数自动分配索引。**Automatic Parameter Indexes **
- 在 picocli 4.3 之前,单值位置参数的默认索引也是
Mixing Options and Positional Parameters
例
class Mixed {
@Parameters
List<String> positional;
@Option(names = "-o")
List<String> options;
}
testcase
String[] args = {
"param0", "-o", "AAA", "param1", "param2", "-o", "BBB", "param3" };
Mixed mixed = new Mixed();
new CommandLine(mixed).parseArgs(args);
assert mixed.positional.equals(Arrays.asList("param0", "param1", "param2", "param3");
assert mixed.options.equals (Arrays.asList("AAA", "BBB"));
Double dash (–)
当命令行参数之一只是两个破折号而没有附加任何字符 (--
) 时,picocli 将所有后续参数解释为Positional Parameters
,甚至是与选项名称匹配的参数
例
class DoubleDashDemo {
@Option(names = "-v") boolean verbose;
@Option(names = "-files") List<String> files;
@Parameters List<String> params;
}
testcase
String[] args = {
"-v", "--", "-files", "file1", "file2" };
DoubleDashDemo demo = new DoubleDashDemo();
new CommandLine(demo).parseArgs(args);
assert demo.verbose;
assert demo.files == null;
assert demo.params.equals(Arrays.asList("-files", "file1", "file2"));
@-files
长命令行的参数文件
假设有文件:/home/foo/args
,内容如下
# This line is a comment and is ignored.
ABC -option=123
'X Y Z'
执行命令: java MyCommand @/home/foo/args
等价于执行:java MyCommand ABC -option=123 "X Y Z"
encoding
若要执行命令: java -DFile.encoding=UTF8 MyCommand ABC -option=123 "X Y Z"
可以通过:
SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
java MyCommand ABC -option=123 "X Y Z"
@-files Usage Help: showAtFileInUsageHelp
@Command(name = "myapp", showAtFileInUsageHelp = true,
mixinStandardHelpOptions = true, description = "Example command.")
class MyApp {
@Parameters(description = "A file.") File file;
}
执行命令结果如下:
Usage: myapp [-hV] [@<filename>...] <file>
Example command.
[@<filename>...] One or more argument files containing options.
<file> A file.
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
增加了-h -V
选项。
Subcommands
Subcommands复杂的命令行工具,例如著名的 git 工具,有许多子命令(例如,commit、push 等),每个子命令都有自己的一组选项和位置参数。 Picocli 使得使用subcommands
和sub-subcommands
的命令变得非常容易,达到任何深度。
例1
@Command(name = "foo", subcommands = Bar.class) class Foo implements Callable<Integer> { @Option(names = "-x") int x; @Override public Integer call() { System.out.printf("hi from foo, x=%d%n", x); boolean ok = true; return ok ? 0 : 1; // exit code } public static void main(String... args) { int exitCode = new CommandLine(new Foo()).execute(args); System.exit(exitCode); } } @Command(name = "bar", description 元器件数据手册
、IC替代型号,打造电子元器件IC百科大全!