jcommander
jcommander copied to clipboard
Prompt for password is shown even when --help is used
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:
- If the user types
--help
, they will be first prompted for the password where they can press Enter, and then they will get the command line usage.
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.
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.
@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 );