Picocli 2.0:事半功倍

介绍

Picocli是一个单文件命令行解析框架,它使您几乎不需要任何代码即可创建命令行应用程序。 使用@Option@Parameters注释应用程序中的字段,picocli将分别使用命令行选项和位置参数填充这些字段。 例如:

@Command(name = "Greet", header = "%n@|green Hello world demo|@")
class Greet implements Runnable {

  @Option(names = {"-u", "--user"}, required = true, description = "The user name.")
  String userName;

  public void run() {
    System.out.println("Hello, " + userName);
  }

  public static void main(String... args) {
    CommandLine.run(new Greet(), System.err, args);
  }
}

当我们执行该程序时,picocli会在调用run方法之前解析命令行并填充userName字段:

$ java Greet -u picocli

Hello, picocli

Picocli生成具有Ansi颜色和样式的使用帮助消息。 如果我们在输入无效的情况下运行上述程序(缺少必需的用户名选项),picocli将显示错误和使用帮助消息:

Picocli可以生成自动完成脚本,该脚本允许最终用户使用<TAB>命令行完成功能来发现可用的选项和子命令。 您可能还喜欢picocli对子命令嵌套子子命令的任何深度的支持。

用户手册详细介绍了picocli的功能。 本文重点介绍了picocli 2.0版本引入的新功能和值得注意的功能。

带有位置参数的混合选项

解析器得到了改进,现在可以在命令行中将位置参数与选项混合使用。

以前,位置参数必须遵循选项。 从此版本开始,任何不是选项或子命令的命令行参数都将被解释为位置参数。

例如:

class MixDemo implements Runnable {
  @Option(names = "-o")
  List<String> options;

  @Parameters
  List<String> positional;

  public void run() {
    System.out.println("positional: " + positional);
    System.out.println("options   : " + options);
  }

  public static void main(String[] args) {
    CommandLine.run(new MixDemo(), System.err, args);
  }
}

通过混合使用选项和位置参数来运行上述类,表明将非选项识别为位置参数。 例如:

$ java MixDemo param0 -o AAA param1 param2 -o BBB param3

positional: [param0, param1, param2, param3]
options   : [AAA, BBB]

为了支持带有位置参数的混合选项,解析器已更改。 从picocli 2.0开始, 默认情况下 ,多值选项(数组,列表和地图字段) 不再贪婪 。 2.0发行说明详细描述了此更改和其他可能的重大更改

发现集合类型

Picocli执行将命令行参数自动类型转换为带注释字段的类型。 命名选项和位置参数都可以强类型化。

在v2.0之前,picocli需要在CollectionMap字段中标注type属性,以便能够进行类型转换。 对于其他类型的字段,例如数组字段和intjava.io.File字段等单值字段,picocli会自动从字段类型中检测目标类型,但是集合和映射需要更多详细的注释。 例如:

class Before {
    @Option(names = "-u", type = {TimeUnit.class, Long.class})
    Map<TimeUnit, Long> timeout;

    @Parameters(type = File.class)
    List<File> files;
}

从v2.0开始,对于CollectionMap字段,不再需要type属性:picocli将根据通用类型推断出collection元素的类型。 type属性仍然像以前一样工作,在大多数情况下只是可选的。

省略type属性可消除某些重复,并产生更简单,更简洁的代码:

class Current {
    @Option(names = "-u")
    Map<TimeUnit, Long> timeout;

    @Parameters
    List<File> files;
}

在上面的示例中,picocli 2.0能够自动发现将命令行参数添加到列表之前需要将其转换为File ,对于地图而言,需要将键转换为TimeUnit并将值转换为Long

自动帮助

Picocli提供了许多方便的方法,例如runcall ,它们可以解析命令行参数,处理错误并调用接口方法来执行应用程序。

从此版本开始,当用户在命令行上指定带有versionHelpusageHelp属性注释的选项时,便捷方法还将自动打印使用帮助和版本信息。

下面的示例程序演示了自动帮助:

@Command(version = "Help demo v1.2.3", header = "%nAutomatic Help Demo%n",
         description = "Prints usage help and version help when requested.%n")
class AutomaticHelpDemo implements Runnable {

    @Option(names = "--count", description = "The number of times to repeat.")
    int count;

    @Option(names = {"-h", "--help"}, usageHelp = true,
            description = "Print usage help and exit.")
    boolean usageHelpRequested;

    @Option(names = {"-V", "--version"}, versionHelp = true,
            description = "Print version information and exit.")
    boolean versionHelpRequested;

    public void run() {
        // NOTE: code like below is no longer required:
        //
        // if (usageHelpRequested) {
        //     new CommandLine(this).usage(System.err);
        // } else if (versionHelpRequested) {
        //     new CommandLine(this).printVersionHelp(System.err);
        // } else { ... the business logic

        for (int i = 0; i < count; i++) {
            System.out.println("Hello world");
        }
    }

    public static void main(String... args) {
        CommandLine.run(new AutomaticHelpDemo(), System.err, args);
    }
}

-h--help一起执行时,程序将输出用法帮助:

类似地,当使用-V--version执行时,程序将输出版本信息:

自动打印帮助的方法:

  • CommandLine :: call
  • 命令行::运行
  • CommandLine :: parseWithHandler(带有内置的Run ...处理程序)
  • CommandLine :: parseWithHandlers(带有内置的Run ...处理程序)

不会自动打印帮助的方法:

  • CommandLine :: parse
  • 命令行:: populateCommand

更好的子命令支持

此版本添加了新的CommandLine::parseWithHandler方法。 这些方法提供了与runcall方法相同的易用性,但具有更大的灵活性,并且更好地支持嵌套子命令。

考虑带有子命令的应用程序需要做什么:

  1. 解析命令行。
  2. 如果用户输入无效,则在解析失败的地方为子命令打印错误消息和用法帮助消息。
  3. 如果解析成功,请检查用户是否为顶级命令或子命令请求了使用帮助或版本信息。 如果是这样,请打印所需的信息并退出。
  4. 否则,执行业务逻辑。 通常,这意味着执行最特定的子命令。

Picocli提供了一些构建基块来完成此任务,但应由应用程序将它们连接在一起。 该接线本质上是样板,在应用程序之间非常相似。 例如,以前,带有子命令的应用程序通常包含如下代码:

public static void main() {
    // 1. parse the command line
    CommandLine top = new CommandLine(new YourApp());
    List<CommandLine> parsedCommands;
    try {
        parsedCommands = top.parse(args);
    } catch (ParameterException ex) {
        // 2. handle incorrect user input for one of the subcommands
        System.err.println(ex.getMessage());
        ex.getCommandLine().usage(System.err);
        return;
    }
    // 3. check if the user requested help
    for (CommandLine parsed : parsedCommands) {
        if (parsed.isUsageHelpRequested()) {
            parsed.usage(System.err);
            return;
        } else if (parsed.isVersionHelpRequested()) {
            parsed.printVersionHelp(System.err);
            return;
        }
    }
    // 4. execute the most specific subcommand
    Object last = parsedCommands.get(parsedCommands.size() - 1).getCommand();
    if (last instanceof Runnable) {
        ((Runnable) last).run();
    } else if (last instanceof Callable) {
        Object result = ((Callable) last).call();
        // ... do something with result
    } else {
        throw new ExecutionException("Not a Runnable or Callable");
    }
}

这是很多样板代码。 Picocli 2.0提供了一种便捷的方法,使您可以将以上所有内容简化为一行代码,以便您可以专注于应用程序的业务逻辑:

public static void main() {
    // This handles all of the above in one line:
    // 1. parse the command line
    // 2. handle incorrect user input for one of the subcommands
    // 3. automatically print help if requested
    // 4. execute one or more subcommands
    new CommandLine(new YourApp()).parseWithHandler(new RunLast(), System.err, args);
}

新的便捷方法是parseWithHandler 。 您可以创建自己的自定义处理程序,也可以使用内置处理程序之一。 Picocli提供了一些常见用例的处理程序实现。

内置的处理程序是RunFirstRunLastRunAll 。 所有这些都提供了自动帮助:如果用户请求了useHelpHelp或versionHelp,则将打印所请求的信息,并且处理程序将返回而无需进一步处理。 处理程序希望所有命令都实现java.lang.Runnablejava.util.concurrent.Callable

  • RunLast执行最特定的命令或子命令。 例如,如果用户调用了java Git commit -m "commit message" ,则picocli会将Git视为顶级命令并commit一个子命令。 在此示例中, commit子命令是最特定的命令,因此RunLast仅执行该子命令。 如果没有子命令,则执行顶层命令。 RunLast现在由picocli内部使用,用于执行现有CommandLine::runCommandLine::call方便的方法。
  • RunFirst仅执行first ,顶层命令,而忽略子命令。
  • RunAll执行顶层命令和出现在命令行中的所有子命令。

还有一个parseWithHandlers方法,该方法与此类似,但另外,您还可以为错误的用户输入指定自定义处理程序。

改进的

现在, CommandLine::callCommandLine::run便捷方法支持子命令,并将执行用户指定的最后一个子命令。 以前,子命令将被忽略,仅执行顶层命令。

改进的例外

最后,从该版本开始,所有picocli异常都提供了getCommandLine方法,该方法在解析或执行失败的情况下返回命令或子命令。 以前,如果用户为带有子命令的应用程序提供了无效的输入,则很难准确地指出哪个子命令未能解析输入。

结论

如果您已经在使用picocli,则v2.0是必不可少的升级。 如果您以前从未使用过picocli,希望以上内容使您有兴趣尝试一下。

其中许多改进源自用户反馈和后续讨论。 请随时在picocli 问题跟踪器上提问,索取功能或提供其他反馈。

如果愿意,请在GitHub上项目加注星标,然后告诉您的朋友!

翻译自: https://www.javacodegeeks.com/2018/01/picocli-2-0-less.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值