1

I am using Pico CLI v4.0.0-alpha-3 & jline v3. I have the following class (using annotations). When I run the main class, I cannot seem to run the command and get the callable invoked. I can invoke the callable if I simply pass in the parameters.

@CommandLine.Command(name = "Test CLI",
        description = "CLI tool",
        header = "%n@|green test cli |@",
        footer = {"",
                "@|cyan Press Ctrl-D to exit the CLI.|@",
                        ""},
        version = "1.0.0",
        showDefaultValues = true,
        optionListHeading = "@|bold %nOptions|@:%n",
        subcommands = {
                Sync.class,
                Status.class
    })
public class Tester implements Callable<Integer> {

    private static final String PROMPT = "Test CLI> ";

    private LineReaderImpl lineReader;
    private PrintWriter out;


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

    @Option(names = {"-p", "--password"}, required = true, description = "password")
    private String password;



   final Tester tester = new Tester();
    final CommandLine cmd = prepareCommand(tester);
    final int exitCode = cmd.execute(args);

When I run the java application, and run the command with the parameters, the callable does not get invoked. When I simply pass in the parameters, the callable gets invoked. Any thoughts on how to fix this so that the CLI user has to pass in the command followed by the parameters.

Remko Popma
  • 35,130
  • 11
  • 92
  • 114
ali haider
  • 19,175
  • 17
  • 80
  • 149
  • Can you show examples of how exactly you are trying to invoke the command? – Remko Popma Jun 06 '19 at 20:41
  • @RemkoPopma I added the call. When i run the CLI application, the call only seems to be invoked when i pass in parameters (e.g. -u abc -p ab123) but not as Test CLI -u abc -p abc123 – ali haider Jun 06 '19 at 21:20
  • @RemkoPopma I have also tried using the jline v3 example referenced here: https://github.com/remkop/picocli/tree/master/picocli-shell-jline3 – ali haider Jun 06 '19 at 22:33
  • Understood now. I’ll post an answer a bit later. – Remko Popma Jun 06 '19 at 23:01
  • Thanks for asking this question! I never realized but this can be quite confusing. I’ll update the documentation for the `picocli-shell-*` modules to explain this better. – Remko Popma Jun 08 '19 at 02:38

2 Answers2

1

I was never able to use the command name once the CLI terminal was open. I could just use the parameters without the command name.

I ended up moving the logic to a subcommand after which the subcommand can be invoked. However, now the CLI displays the suggested command as "parent command + sub command" which I can see is quite confusing (and using that option does not produce the desired outcome). I ended up removing the description from the parent command to take care of it.

ali haider
  • 19,175
  • 17
  • 80
  • 149
1

Command Line Utilities

I think that the way that java applications are packaged is what causes the confusion. If your application was a single executable file, let's say myapp.exe, then you would invoke the app on the command line like this:

myapp -u xxx

If your application is a Java class, you need to invoke it like this:

java -cp mylib.jar com.myorg.MyApp -u xxx

So, the top-level command is the com.myorg.MyApp class itself, there is no need to specify myapp as an argument to the com.myorg.MyApp class on the command line.

Now imagine that your myapp application has a subcommand, called sub. Again, if your application was a single executable file, you would invoke the subcommand on the command line like this:

myapp sub --subcommand-options

As a Java class, you need to invoke it like below. Note that here, you do need to specify the subcommand name as a command line argument to the com.myorg.MyApp class:

java -cp mylib.jar com.myorg.MyApp sub --subcommand-options

Side note: What people often do is create startup scripts for Windows and unix that allow you to execute your application as myapp instead of java -cp mylib.jar com.myorg.MyApp. One of the reasons that people are enthusiastic about GraalVM is that GraalVM native images solve this packaging problem and allow Java developers to distribute their applications as a single executable file.

Picocli's main use case is to facilitate creating command line utilities in Java. So it is very easy to do the above with code like this:

package com.myorg;

@Command(name = "myapp")
public class MyApp implements Runnable {

    @Option(names = {"-u", "--username"}, description = "user name")
    private String userName;

    @Command // subcommand "sub"
    public void sub(@Option(names = "--subcommand-options") String option)  {
        System.out.printf("sub says hello %s!%n", userName);
    }

    @Override
    public void run() {
        System.out.printf("myapp says hi %s!%n", userName);
    }

    public static void main(String[] args) {
        int exitCode = new CommandLine(new MyApp()).execute(args);

    }
}

Interactive Command Line Applications

When you create an interactive CLI shell application with JLine, you need to keep in mind that the top-level command is not specified on the command line.

Therefore, all the commands that you want users to be able to type in the shell should be subcommands, like you describe in your answer.

The top-level command can still be useful to provide help to the users. One idea is to print a friendly help message followed by the usage help of the top-level command when the user enters an invalid command (or just hits enter without parameters).

Remko Popma
  • 35,130
  • 11
  • 92
  • 114