https://picocli.info/#_introduction
前言
相信每个Java程序员都用过Scanner
,兴奋地写了一个命令行程序。 但是,使用命令行程序也很实用Java编写一个强大的命令程序并不容易,主要有以下痛点:
- 没有成熟的框架
包装参数接收、参数提示、参数验证
- 很难处理
参数的互斥
以及特定命令的相互依赖 - 无法进行
命令自动补全
- 由于JVM解释执行字节码,并解释执行字节码JIT不能在短期执行中发挥作用,Java慢慢启动命令行程序
- 集成SpringBoot以及其他组件,
启动更慢
这些问题都可以使用Picocli来解决
引用:https://blog.csdn.net/qq_40419564/article/details/115290878
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.picocli</groupId> <artifactId>picocli</artifactId> <version>4.6.3</version> </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>demo1</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.jhs.CheckSum</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行命令mvn clean package
Command
> java -jar target/demo1-jar-with-dependencies.jar
Missing required parameter: '<file>'
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;
}
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) {
/* ... */ }
}
执行命令: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
}
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;
}
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;
}
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"
若要执行命令: 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 标签:
jhs200t电阻器jhs保电阻电缆