Java 带Picocli的CLI:在调用子命令之前调用main命令

Java 带Picocli的CLI:在调用子命令之前调用main命令,java,command-line-interface,picocli,Java,Command Line Interface,Picocli,由于子命令支持(以及基于注释的声明),我从Apache Commons CLI切换到Picocli 考虑一个命令行工具,如git,其中包含子命令,如push。Git有一个主开关--verbose或-v,用于在所有子命令中启用verbose模式。 如何实现在任何子命令之前执行的主开关 这是我的测试 @CommandLine.Command(name = "push", description = "Update remote refs along with associated o

由于子命令支持(以及基于注释的声明),我从Apache Commons CLI切换到Picocli

考虑一个命令行工具,如
git
,其中包含子命令,如
push
。Git有一个主开关
--verbose
-v
,用于在所有子命令中启用verbose模式。 如何实现在任何子命令之前执行的主开关

这是我的测试

@CommandLine.Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand implements Callable<Void> {
    @Override
    public Void call() throws Exception {
        System.out.println("#PushCommand.call");

        return null;
    }
}

@CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp implements Callable<Void> {
    @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
    private boolean usageHelpRequested;

    @CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
    private boolean verboseMode;

    public static void main(String[] args) {
        GitApp app = new GitApp();
        CommandLine.call(app, "--verbose", "push");
        System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
    }

    @Override
    public Void call() throws Exception {
        System.out.println("#GitApp.call");

        return null;
    }
}

我希望在调用子命令之前调用
GitApp.call
get。但是只有sub命令被调用。

因为Picocli支持带有选项的继承,我已经将
--help
--verbose
选项提取到一个抽象类
BaseCommand
中,并从子命令调用
super.call

abstract class BaseCommand implements Callable<Void> {
    @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
    private boolean usageHelpRequested;

    @CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
    private boolean verboseMode;

    @Override
    public Void call() throws Exception {
        if (verboseMode) {
            setVerbose();
        }
        return null;
    }

    private void setVerbose() {
        System.out.println("enter verbose mode");
    }
}

@CommandLine.Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand extends BaseCommand {
    @Override
    public Void call() throws Exception {
        super.call();
        System.out.println("Execute push command");
        return null;
    }
}

@CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp extends BaseCommand {
    public static void main(String[] args) {
        GitApp app = new GitApp();
        CommandLine.call(app, "push", "--verbose");
    }

    @Override
    public Void call() throws Exception {
        super.call();
        System.out.println("GitApp.call called");
        return null;
    }
}
抽象类BaseCommand实现可调用{
@选项(名称={“-h”,“--help”},usageHelp=true,description=“显示此帮助消息”)
私有布尔用法helprequest;
@选项(名称={“-v”,“--verbose”},description=“verbose mode.有助于故障排除。”)
私有布尔详细模式;
@凌驾
public Void call()引发异常{
如果(详细模式){
setVerbose();
}
返回null;
}
私有void setVerbose(){
System.out.println(“进入详细模式”);
}
}
@CommandLine.Command(name=“push”,
description=“随关联对象一起更新远程参照”)
类PushCommand扩展了BaseCommand{
@凌驾
public Void call()引发异常{
super.call();
System.out.println(“执行推送命令”);
返回null;
}
}
@Command(description=“Version control”,子命令={PushCommand.class})
公共类GitApp扩展BaseCommand{
公共静态void main(字符串[]args){
GitApp app=新的GitApp();
CommandLine.call(app,“push”,“verbose”);
}
@凌驾
public Void call()引发异常{
super.call();
System.out.println(“调用GitApp.call”);
返回null;
}
}

命令行.call(和
命令行.run
)方法只调用by design,因此您在原始帖子中看到的是预期的行为

调用
运行
方法实际上是一种快捷方式。以下两行是等效的:

CommandLine.run(callable, args); // internally uses RunLast, equivalent to: 
new CommandLine(callable).parseWithHandler(new RunLast(), args);
更新:从picocli 4.0开始,上述方法已被弃用,并替换为
新命令行(myapp).execute(args)
。“处理程序”现在称为“执行策略”(下面的示例)

还有一个运行所有匹配命令的。以下
main
方法给出了所需的行为:

public static void main(String[] args) {
    args = new String[] { "--verbose", "push" };
    GitApp app = new GitApp();
    // before picocli 4.0:
    new CommandLine(app).parseWithHandler(new RunAll(), args);
    // from picocli 4.0:
    //new CommandLine(app).setExecutionStrategy(new RunAll()).execute(args);
    System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}
输出:

#GitApp.call
#PushCommand.call
#GitApp.main after. verbose: true
您可能还对注释感兴趣。这告诉picocli将父命令的实例注入子命令。然后,子命令可以调用父命令上的方法,例如检查
verbose
是否为true。例如:

更新:从picocli 4.0中,使用
setExecutionStrategy
方法指定
RunAll
。下面的示例更新为使用新的picocli 4.0+API


其他次要编辑:通过导入内部类,使注释更加紧凑。您可能还喜欢有助于减少样板代码的属性和内置子命令。

非常好的提示!也谢谢你的皮科利!
#GitApp.call
#PushCommand.call
#GitApp.main after. verbose: true
import picocli.CommandLine;
import picocli.CommandLine.*;

@Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand implements Runnable {

    @ParentCommand // picocli injects the parent instance
    private GitApp parentCommand;

    public void run() {
        System.out.printf("#PushCommand.call: parent.verbose=%s%n",
                parentCommand.verboseMode); // use parent instance
    }
}

@Command(description = "Version control",
        mixinStandardHelpOptions = true, // auto-include --help and --version
        subcommands = {PushCommand.class,
                       HelpCommand.class}) // built-in help subcommand
public class GitApp implements Runnable {
    @Option(names = {"-v", "--verbose"},
            description = "Verbose mode. Helpful for troubleshooting.")
    boolean verboseMode;

    public void run() {
        System.out.println("#GitApp.call");
    }

    public static void main(String[] args) {
        args = new String[] { "--verbose", "push" };

        GitApp app = new GitApp();
        int exitCode = new CommandLine(app)
            .setExecutionStrategy(new RunAll())
            .execute(args);

        System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
        System.exit(exitCode);
    }
}