cbeust / jcommander

Command line parsing framework for Java
Apache License 2.0
1.96k stars 334 forks source link

Prompt for password is shown even when --help is used #460

Open perlun opened 5 years ago

perlun commented 5 years ago

Thanks for a great library. My scenario: I am writing a command line tool that requires a password to be provided.

If I specify the required = true setting in the @Parameter annotation for my password field, the user will have to type --password which isn't really what I want - I want them to always be asked for the password, unless the password is set using an environment variable.

Because of this, I've come up with an approach that looks like below:

    @Parameter(names = { "-h", "--help" }, help = true)
    protected boolean help;

    @Parameter(names = "--password", description = "Connection password", password = true)
    protected String password = System.getenv("THE_PASSWORD");

    void someMethod(String[] args) {
        List<String> argsList = Lists.newArrayList( args );

        if ( password == null ) {
            // Environment variable not set - add to list of arguments.
            argsList.add( "--password" );
        }

        JCommander jCommander = JCommander.newBuilder()
                .addObject( this )
                .build();

        jCommander.parse( modifiedArgs );

        if (help) {
            jCommander.usage();
        }

...i.e, add a "fake" argument --password to the list of argument being passed to JCommander.

This works reasonably well for most scenarios, but I've seen at least one such where it does not:

I believe this is flawed - the help = true argument should take precedence over all password = true arguments. If the user asked for help, it does not make much sense to force them to provide all password-flagged arguments (even if they typed --password on the command line, or as in my case, it was injected by the program) - since execution of the program will be aborted anyway.

If you have some other suggestion(s), I'm open for it.

remkop commented 5 years ago

You get use the System.console().readPassword() API in your application.

With picocli this would look something like this (JCommander should be fairly similar):

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "app", mixinStandardHelpOptions = true,
        description = "Password example app", version = "1.0")
public class PasswordExample implements Runnable {

    @Option(names = "--password", description = "Connection password", interactive = true)
    protected String password = System.getenv("THE_PASSWORD");

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

    public void run() {
        if (password == null) {
            password = new String(System.console().readPassword("Connection password: "));
        }
        System.out.printf("Your password is: %s", password);
    }
}

If I start the app without arguments, it prompts me for the password. When the environment variable is defined, it uses that value as the password without prompting.

With --help, it shows this:

Usage: app [-hV] [--password=<password>]
Password example app
      --password=<password>
                  Connection password
  -h, --help      Show this help message and exit.
  -V, --version   Print version information and exit.
navneet-kumar commented 5 years ago
    @Parameter(names = { "-h", "--help" }, help = true)
    protected boolean help;

    @Parameter(names = "--password", description = "Connection password", password = true)
    protected String password = System.getenv("THE_PASSWORD");

    void someMethod(String[] args) {
        List<String> argsList = Lists.newArrayList( args );

        // check if it's a help request here itself, since password argument is not added it wont ask for password
        if (help) {
            jCommander.usage();
        }

        if ( password == null ) {
            // Environment variable not set - add to list of arguments.
            argsList.add( "--password" );
        }

        JCommander jCommander = JCommander.newBuilder()
                .addObject( this )
                .build();

        jCommander.parse( modifiedArgs );