资讯详情

picocli-入门

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可以分为optionspositional 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 Optionspositional 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 **

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 使得使用subcommandssub-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保电阻电缆

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台