Jay Taylor's notes
back to listing indexpicocli - a mighty tiny command line interface
[web search]picocli - a mighty tiny command line interface
boolean
0..1
Boolean options by default don’t require an option parameter. The field is set to the opposite of its default value when the option name is recognized. (This can be configured.)
Single-valued type (e.g., int
, String
, File
)
1
The option name must be followed by a value.
Multi-valued type (arrays, collections or maps)
1
The option name must be followed by a value.
|
Prior to picocli 2.0, multi-valued options used to greedily consume as many arguments as possible until
encountering another option or subcommand.
If your application relies on the previous behaviour, you need to explicitly specify an option arity of 0..* when migrating to picocli 2.0.
|
6.5.2. Positional Parameter Arity
@Parameters Field Type | Default Arity | Notes |
---|---|---|
boolean |
1 |
Positional parameters of type |
Single-valued type (e.g., |
1 |
One parameter required for each position. |
Multi-valued type (arrays, collections or maps) |
0..1 |
For multi-valued positional parameters (arrays, collections or maps), values are optional, not required. |
@Parameters
fields are applied to a command line argument if their index matches the argument’s position.
The default index is *
, meaning all positions.
A @Parameters
field with index = "*"
is applied multiple times: once for each positional parameter on the command line.
When a @Parameters
field is applied (because its index matches the index of the positional parameter), the field may consume zero, one or more arguments, depending on its arity.
6.6. Optional Values
6.6.1. Optional Option Parameters
When an option is defined with arity = "0..1"
, it may or not have a parameter value.
The fallback value determines what value is assigned when the option is specified without a value, while the default value determines what value is assigned when the option is not specified at all.
6.6.2. Optional Parameter Use Cases
This feature is commonly used when an application wants to combine two options into one: the presence or absence of the option can be used like a boolean flag to trigger some behaviour, and the option value can be used to modify this behaviour.
An example use case is an option that switches on logging when present, with an optional value to set the log level. For example:
java@Option(names = "--syslog", defaultValue = "OFF", fallbackValue = "INFO",
description = {
"When specified without arguments, start sending syslog messages at INFO level.",
"If absent, no messages are sent to syslog.",
"Optionally specify a severity value. Valid values: ${COMPLETION-CANDIDATES}."})
MyLogLevel syslogLevel;
Another example use case is password options.
6.6.3. Optional Parameter Limitations
Be careful when defining commands that have both an option with an optional parameter and a positional parameter.
|
The picocli parser is "greedy" when it handles optional parameters: it looks at the value following the option name, and if that value is likely to be a parameter (not another option or subcommand) then it will process the value as a parameter for that option. This may not always be what you want. |
For example:
javaclass Ambiguous {
@Parameters(description = "The file (required).")
File file;
@Option(names = "-x", arity = "0..1",
description = "Option with optional parameter")
String value;
}
When -x VALUE
is specified on the command line, this results in an error: Missing required parameter: <file>
.
Users can use the end-of-options delimiter and disambiguate the input with
-x ‐‐ VALUE
, but this may not be obvious to many users.
One idea is to show the end-of-options delimiter in the usage help.
Another idea is to make use of the IParameterPreprocessor Parser Plugin introduced with picocli 4.6.
An alternative is to avoid the use of optional parameters and use the default arity in this scenario to eliminate the ambiguity altogether.
7. Required Arguments
7.1. Required Options
Options can be marked required
to make it mandatory for the user to specify them on the command line. When a required option is not specified, a MissingParameterException
is thrown from the parse
method. For example:
javaclass MandatoryOption {
@Option(names = "-n", required = true, description = "mandatory number")
int number;
@Parameters
File[] files;
}
The following command line arguments would result in an exception complaining that number
is missing:
// invalid: missing option -n <command> file1 file2 file3
The following command line arguments would be accepted:
// valid: required option -n has a value <command> -n 123 file1 file2 file3
7.2. Required Parameters
Single-value @Parameters
are always mandatory, because single-value positional parameters have arity = "1"
by default.
The arity
attribute can be used to make multi-value @Parameters
mandatory:
javaclass BothOptionAndParametersMandatory {
@Parameters(arity = "1..*", description = "at least one File")
File[] files;
@Option(names = "-n", required = true, description = "mandatory number")
int number;
}
The following command line arguments would result in an exception complaining that files
are missing:
// invalid: missing file parameters <command> -n 123
The following command line arguments would be accepted:
// valid: both required fields have a value <command> -n 123 file1
7.3. Options with an Optional Parameter
See Optional Values.
8. Argument Groups
Picocli 4.0 introduces a new @ArgGroup
annotation and its ArgGroupSpec
programmatic equivalent.
Argument Groups can be used to define:
-
mutually exclusive options
-
options that must co-occur (dependent options)
-
option sections in the usage help message
-
repeating composite arguments
To create a group using the annotations API, annotate a field or method with @ArgGroup
.
The field’s type refers to the class containing the options and positional parameters in the group.
(For annotated interface methods this would be the return type, for annotated setter methods in a concrete class this would be the setter’s parameter type.)
Picocli will instantiate this class when needed to capture command line argument values in the @Option
and @Parameters
-annotated fields and methods of this class.
|
Inherited Options currently cannot be used in Argument Groups. Applications that want to reuse Argument Groups across subcommands need to use Mixins. See this examplehttps://github.com/remkop/picocli/blob/main/picocli-examples/src/main/java/picocli/examples/arggroup/ArgGroupMixinDemo2.java for sharing an Argument Group defining global options between subcommands. |
8.1. Mutually Exclusive Options
Annotate a field or method with @ArgGroup(exclusive = true)
to create a group of mutually exclusive options and positional parameters. For example:
java@Command(name = "exclusivedemo")
public class MutuallyExclusiveOptionsDemo {
@ArgGroup(exclusive = true, multiplicity = "1")
Exclusive exclusive;
static class Exclusive {
@Option(names = "-a", required = true) int a;
@Option(names = "-b", required = true) int b;
@Option(names = "-c", required = true) int c;
}
}
The above example defines a command with mutually exclusive options -a
, -b
and -c
.
The group itself has a multiplicity
attribute that defines how many times the group may be specified within the command.
The default is multiplicity = "0..1"
, meaning that by default a group may be omitted or specified once.
In this example the group has multiplicity = "1"
, so the group must occur once: one of the exclusive options must occur on the command line.
The synopsis of this command is:
Usage: exclusivedemo (-a=<a> | -b=<b> | -c=<c>)
When one of the options in the group is matched, picocli creates an instance of the Exclusive
class and assigns it to the @ArgGroup
-annotated exclusive
field.
Note that the options are defined as required = true
; this means required within the group, not required within the command.
|
As of picocli 4.1.2, all options in an exclusive group are automatically considered required, even if they are not marked as required = true in the annotations. Applications using older versions of picocli should mark all options in exclusive groups as required.
|
Picocli will validate the arguments and throw a MutuallyExclusiveArgsException
if multiple mutually exclusive arguments were specified. For example:
javaMutuallyExclusiveOptionsDemo example = new MutuallyExclusiveOptionsDemo();
CommandLine cmd = new CommandLine(example);
try {
cmd.parseArgs("-a=1", "-b=2");
} catch (MutuallyExclusiveArgsException ex) {
assert "Error: -a=<a>, -b=<b> are mutually exclusive (specify only one)"
.equals(ex.getMessage());
}
For the above group, only one of the options can be specified. Any other combination of options, or the absence of options, is invalid.
|
Picocli will not initialize the @ArgGroup -annotated field
if none of the group options is specified on the command line.
For optional groups (groups with multiplicity = "0..1" - the default) this means that the @ArgGroup -annotated field may remain null .
|
8.2. Mutually Dependent Options
8.2.1. Overview
Annotate a field or method with @ArgGroup(exclusive = false)
to create a group of dependent options and positional parameters that must co-occur. For example:
java@Command(name = "co-occur")
public class DependentOptionsDemo {
@ArgGroup(exclusive = false)
Dependent dependent;
static class Dependent {
@Option(names = "-a", required = true) int a;
@Option(names = "-b", required = true) int b;
@Option(names = "-c", required = true) int c;
}
}
The above example defines a command with dependent options -a
, -b
and -c
that must co-occur.
The group itself has a multiplicity
attribute that defines how many times the group may be specified within the command.
In this example the group uses the default multiplicity, multiplicity = "0..1"
, meaning that the group may be omitted or specified once.
The synopsis of this command is:
Usage: co-occur [-a=<a> -b=<b> -c=<c>]
When the first option in the group is matched, picocli creates an instance of the Dependent
class and assigns it to the @ArgGroup
-annotated dependent
field.
Note that the options are defined as required = true
; this means required within the group, not required within the command.
Picocli will validate the arguments and throw a MissingParameterException
if not all dependent arguments were specified. For example:
javaDependentOptionsDemo example = new DependentOptionsDemo();
CommandLine cmd = new CommandLine(example);
try {
cmd.parseArgs("-a=1", "-b=2");
} catch (MissingParameterException ex) {
assert "Error: Missing required argument(s): -c=<c>".equals(ex.getMessage());
}
|
Picocli will not initialize the @ArgGroup -annotated field
if none of the group options is specified on the command line.
For optional groups (groups with multiplicity = "0..1" - the default) this means that the @ArgGroup -annotated field may remain null .
|
8.2.2. Non-Required Options in Mutually Dependent Groups
In mutually dependent groups it is possible to have one or more options that are not required. This is different from exclusive groups, where all options are always required.
It is useful to be able to define a co-occurring group as (-a -b [-c])
so that both -a -b -c
and -a -b
are valid on the command line, but not -a -c
for example.
This can be implemented by marking the optional option with required = false
, as in the below example:
java@Command(name = "co-occur-with-optional-options")
public class DependentWithOptionalOptionsDemo {
@ArgGroup(exclusive = false, multiplicity = "1")
DependentWithOptionalOptions group;
static class DependentWithOptionalOptions {
@Option(names = "-a", required = true) int a;
@Option(names = "-b", required = true) int b;
@Option(names = "-c", required = false) int c;
}
}
More than one option can be optional in mutually dependent groups, but it is recommended to have at least one required option in the group (or there is not much point in using a mutually dependent group).
8.3. Option Sections in Usage Help
8.3.1. Use Heading to Enable Option Sections
The example below uses groups to define options sections in the usage help.
When a group has a non-null heading
(or headingKey
), the options in the group are given the specified heading in the usage help message.
The headingKey
attribute can be used to get the heading text from the command’s resource bundle.
This works for mutually exclusive or co-occurring groups, but it is also possible to define a group that does no validation but only creates an option section in the usage help.
Annotate a field or method with @ArgGroup(validate = false)
to create a group for display purposes only. For example:
java@Command(name = "sectiondemo", description = "Section demo")
public class OptionSectionDemo {
@ArgGroup(validate = false, heading = "This is the first section%n")
Section1 section1;
static class Section1 {
@Option(names = "-a", description = "Option A") int a;
@Option(names = "-b", description = "Option B") int b;
@Option(names = "-c", description = "Option C") int c;
}
@ArgGroup(validate = false, heading = "This is the second section%n")
Section2 section2;
static class Section2 {
@Option(names = "-x", description = "Option X") int x;
@Option(names = "-y", description = "Option Y") int y;
@Option(names = "-z", description = "Option Z") int z;
}
public static void main(String[] args) {
new CommandLine(new OptionSectionDemo()).usage(System.out);
}
}
This prints the following usage help message:
Usage: sectiondemo [-a=<a>] [-b=<b>] [-c=<c>] [-x=<x>] [-y=<y>] [-z=<z>] Section demo This is the first section -a=<a> Option A -b=<b> Option B -c=<c> Option C This is the second section -x=<x> Option X -y=<y> Option Y -z=<z> Option Z
Note that the heading text must end with %n
to insert a newline between the heading text and the first option.
This is for consistency with other headings in the usage help, like @Command(headerHeading = "Usage:%n", optionListHeading = "%nOptions:%n")
.
|
Picocli will not initialize the @ArgGroup -annotated field
if none of the group options is specified on the command line.
For optional groups (groups with multiplicity = "0..1" - the default) this means that the @ArgGroup -annotated field may remain null .
|
8.3.2. Option Section Order
Options that are not in any argument group are always displayed before any group option sections.
The ordering of group option sections can be controlled with the order
attribute. For example:
java@ArgGroup(heading = "First%n", order = 1) Section1 section1;
@ArgGroup(heading = "Next%n", order = 2) Section2 section2;
@ArgGroup(heading = "Last%n", order = 3) Section3 section3;
8.3.3. Validation Trade-offs
Note that setting validate = false
means that picocli won’t validate user input for the group.
For example, even for groups with multiplicity = 1
, when the end user specifies the group multiple times, no error is shown.
If the group is a single-value field, only the last occurrence is stored and previous occurrences are silently dropped.
If validation is needed, the recommendation is to make the field holding the group a collection, and doing Custom Validation. For example to ensure that this collection holds only a single element:
java@ArgGroup(validate = false, heading = "This is the first section%n", multiplicity = "0..1")
private List<Section1> section1List = new ArrayList<>();
@Spec CommandSpec spec;
// validate in the business logic
public void run() {
if (section1List.size() > 1) {
throw new ParameterException(spec.commandLine(),
"Group [-a=<a>] [-b=<b>] [-c=<c>] can be specified at most once.");
}
// remaining business logic...
}
8.4. Repeating Composite Argument Groups
The below example shows how groups can be composed of other groups, and how arrays and collections can be used to capture repeating groups (with a multiplicity
greater than one):
java@Command(name = "repeating-composite-demo")
public class CompositeGroupDemo {
@ArgGroup(exclusive = false, multiplicity = "1..*")
List<Composite> composites;
static class Composite {
@ArgGroup(exclusive = false, multiplicity = "0..1")
Dependent dependent;
@ArgGroup(exclusive = true, multiplicity = "1")
Exclusive exclusive;
}
static class Dependent {
@Option(names = "-a", required = true) int a;
@Option(names = "-b", required = true) int b;
@Option(names = "-c", required = true) int c;
}
static class Exclusive {
@Option(names = "-x", required = true) boolean x;
@Option(names = "-y", required = true) boolean y;
@Option(names = "-z", required = true) boolean z;
}
}
In the above example, the annotated composites
field defines a composite group that must be specified at least once, and may be specified many times (multiplicity = "1..*"
), on the command line. Notice that for multi-value groups the type of the @ArgGroup
-annotated field must be a collection or an array to capture the multiple Composite
instances that hold the values that were matched on the command line.
The synopsis of this command is:
Usage: repeating-composite-demo ([-a=<a> -b=<b> -c=<c>] (-x | -y | -z))...
Each time the group is matched, picocli creates an instance of the Composite
class and adds it to the composites
list.
The Composite
class itself contains two groups: an optional (multiplicity = "0..1"
) group of dependent options that must co-occur, and another group of mutually exclusive options, which is mandatory (multiplicity = "1"
).
The below example illustrates:
javaCompositeGroupDemo example = new CompositeGroupDemo();
CommandLine cmd = new CommandLine(example);
cmd.parseArgs("-x", "-a=1", "-b=1", "-c=1", "-a=2", "-b=2", "-c=2", "-y");
assert example.composites.size() == 2;
Composite c1 = example.composites.get(0);
assert c1.exclusive.x;
assert c1.dependent.a == 1;
assert c1.dependent.b == 1;
assert c1.dependent.c == 1;
Composite c2 = example.composites.get(1);
assert c2.exclusive.y;
assert c2.dependent.a == 2;
assert c2.dependent.b == 2;
assert c2.dependent.c == 2;
|
Picocli will not initialize the |
8.5. Default Values in Argument Groups
The default values of options in an argument group are applied when at least one option in the group is matched on the command line and picocli instantiates the user object of the group.
Picocli will not initialize the @ArgGroup
-annotated field (and so no default values are applied) if none of the group options is specified on the command line.
8.5.1. Showing Default Values in Group Usage Help
Options used in argument groups should define default values via the @Option(defaultValue = "…")
annotation.
When default values are defined in the annotation, the ${DEFAULT-VALUE}
variable can be used to show the default value in the description of options in an argument group. For example:
javaclass GoodGroup {
@Option(names = "-x", defaultValue = "123", description = "Default: ${DEFAULT-VALUE}")
int x;
}
@Command(name = "good", description = "usage help shows the default value")
class GoodExample {
@ArgGroup GoodGroup goodGroup;
public static void main(String[] args) {
new CommandLine(new GoodExample()).usage(System.out);
}
}
When the default value is defined in the annotation, the usage help shows the correct default value:
Usage: good [[-x=<x>]] usage help shows the default value -x=<x> Default: 123
|
Picocli will not be able to retrieve the default values that are defined by assigning a value in the declaration of an Java Kotlin
When the default value is defined in the field declaration and not in the annotation, usage help for the options in the group incorrectly shows
|
8.5.2. Assigning Default Values in Argument Groups
Applications need to do extra work for argument group options with default values. Picocli does not instantiate the group if none of the options in the group is specified on the command line, so applications need to do this manually.
Below are some recommendations for using default values in argument group options and positional parameters:
-
specify default values in both the
@Option
annotation, and in the initial value of the@Option
-annotated field. Yes, that means some duplication. This recommendation also holds for positional@Parameters
. -
the application needs to manually instantiate the
@ArgGroup
-annotated field. More details follow below.
The default value in the @Option
or @Parameters
annotation means that picocli can show the default value in the usage help, and the initial value means that any new instance of the group that contains the option will already have the default value assigned to that option field.
The example below shows an option that defines the default value in the annotation as well as in the initial value of the field:
javaclass MyGroup {
// (group options):
// specify default values both in the annotation and in the initial value
@Option(names = "-x", defaultValue = "XX")
String x = "XX"; // yes, some duplication :-(
}
Next, the application needs to manually instantiate the @ArgGroup
-annotated field.
There is a trade-off:
-
instantiating the
@ArgGroup
-annotated field in the declaration is simple and short but applications cannot easily detect whether a group option was specified on the command line or not -
leaving the
@ArgGroup
-annotated fieldnull
in the declaration allows applications to easily detect whether a group option was specified on the command line, but is a bit more code
The example below shows the first idea: instantiating the group object in the declaration.
This way, the group object is never null
and (if you followed the previous recommendation) all option fields in this group object will have the default value as their initial value.
java// instantiating the group in the declaration:
// all options in this group now also have their initial (default) value
@ArgGroup MyGroup myGroup = new MyGroup();
Alternatively, applications can initialize the group objects in the business logic: in the run
or call
method.
This allows the application to determine whether the user specified a value for any of the options in the group.
The example below demonstrates initializing the group objects in the business logic:
java@Command(name = "group-default-demo")
class MyApp implements Runnable {
@ArgGroup Outer outer; // no initial value
static class Outer {
@Option(names = "-x", defaultValue = "XX") String x = "XX";
@ArgGroup(exclusive = true)
Inner inner; // no initial value
}
static class Inner {
@Option(names = "-a", defaultValue = "AA") String a = "AA";
@Option(names = "-b", defaultValue = "BB") String b = "BB";
}
public void run() {
if (outer == null) { // -x option was not specified on command line
// perform any logic that needs to happen if -x is missing
outer = new Outer(); // assign default values
}
if (outer.inner == null) { // neither -a nor -b was specified
// perform any logic that needs to happen if -a or -b is missing
outer.inner = new Inner(); // assign defaults for inner group
}
// remaining business logic...
}
}
8.6. Positional Parameters
When a @Parameters
positional parameter is part of a group, its index
is the index within the group, not within the command.
Below is an example of an application that uses a repeating group of positional parameters:
java@Command(name = "grades", mixinStandardHelpOptions = true, version = "grades 1.0")
public class Grades implements Runnable {
static class StudentGrade {
@Parameters(index = "0") String name;
@Parameters(index = "1") BigDecimal grade;
}
@ArgGroup(exclusive = false, multiplicity = "1..*")
List<StudentGrade> gradeList;
@Override
public void run() {
gradeList.forEach(e -> System.out.println(e.name + ": " + e.grade));
}
public static void main(String[] args) {
System.exit(new CommandLine(new Grades()).execute(args));
}
}
Running the above program with this input:
Alice 3.1 Betty 4.0 "X Æ A-12" 3.5 Zaphod 3.4
Produces the following output:
Alice: 3.1 Betty: 4.0 X Æ A-12: 3.5 Zaphod: 3.4
8.7. Argument Group Limitations
-
Options with the same name cannot be defined in multiple groups. Similarly, it is not possible to define an option outside of a group with the same name as a different option that is part of a group.
-
Positional parameters in a single group work fine, but take care (or avoid) defining positional parameters in multiple groups or positional parameters in a group as well as outside a group. Positional parameters are matched by index, and while the index of a group is reset when a new group multiple is encountered, the index of positional parameters outside a group only increases and is never reset.
-
Some relationships between options cannot be expressed with picocli argument groups. In general, picocli argument groups can only express relationship for which you can write a command line synopsis with every option occurring only once. For example, it is not possible to use argument groups to create a relationship with exclusive options
[-a | -b]
, where-a
requires another option-c
:[[-a] -c]
, while at the same time-b
is independent of-c
:[-b] [-c]
. The application may need to do some programmatic validation in such cases.
9. Executing Commands
Parsing the command line arguments is the first step. A robust real-world application needs to handle a number of scenarios:
-
User input was invalid
-
User requested usage help (potentially for a subcommand)
-
User requested version help (potentially for a subcommand)
-
None of the above: we can run the business logic (potentially for a subcommand)
-
The business logic may throw an exception
Picocli 4.0 introduces an execute
method for handling all of the above scenarios in a single line of code. For example:
javanew CommandLine(new MyApp()).execute(args);
With the execute
method, application code can be extremely compact:
java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Command(name = "myapp", mixinStandardHelpOptions = true, version = "1.0")
class MyApp implements Callable<Integer> {
@Option(names = "-x") int x;
@Override
public Integer call() { // business logic
System.out.printf("x=%s%n", x);
return 123; // exit code
}
public static void main(String... args) { // bootstrap the application
System.exit(new CommandLine(new MyApp()).execute(args));
}
}
Despite being only 15 lines long, this is a full-fledged application, with --help
and --version
options in addition to the -x
option.
The execute
method will show the usage help or version information if requested by the user, and invalid user input will result
in a helpful error message. If the user input was valid, the business logic is invoked.
Finally, the execute
method returns an exit status code that can be used to call System.exit
if desired.
|
A command is executable if its user object implements Runnable or Callable , or is a @Command -annotated Method . Examples follow below.
|
|
The execute method replaces the older run , call , invoke and parseWithHandlers methods.
|
The DIY Command Execution section shows an example of the boilerplate code that can be omitted with the execute
method.
9.1. Exit Code
Many command line applications return an exit codehttps://en.wikipedia.org/wiki/Exit_status to signify success or failure. Zero often means success, a non-zero exit code is often used for errors, but other than that, meanings differ per application.
The CommandLine.execute
method introduced in picocli 4.0 returns an int
, and applications can use this return value to call System.exit
if desired. For example:
javapublic static void main(String... args) {
int exitCode = new CommandLine(new MyApp()).execute(args);
System.exit(exitCode);
}
|
Older versions of picocli had some limited exit code support where picocli would call System.exit , but this is now deprecated.
|
9.2. Generating an Exit Code
@Command
-annotated classes that implement Callable
and @Command
-annotated methods can simply return an int
or Integer
, and this value will be returned from CommandLine.execute
. For example:
java@Command(name = "greet")
class Greet implements Callable<Integer> {
public Integer call() {
System.out.println("hi");
return 1;
}
// define a "shout" subcommand with a @Command-annotated method
@Command
int shout() {
System.out.println("HI!");
return 2;
}
}
assert 1 == new CommandLine(new Greet()).execute();
assert 2 == new CommandLine(new Greet()).execute("shout");
Commands with a user object that implements Runnable
can implement the IExitCodeGenerator
interface to generate an exit code. For example:
java@Command(name = "wave")
class Gesture implements Runnable, IExitCodeGenerator {
@Override public void run() {
System.out.println("wave");
}
@Override public int getExitCode() {
return 3;
}
}
assert 3 == new CommandLine(new Gesture()).execute();
9.3. Exception Exit Codes
By default, the execute
method returns CommandLine.ExitCode.OK
(0
) on success, CommandLine.ExitCode.SOFTWARE
(1
) when an exception occurred in the Runnable, Callable or command method, and CommandLine.ExitCode.USAGE
(2
) for invalid input. (These are common values according to this StackOverflow answerhttps://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux/40484670#40484670). This can be customized with the @Command
annotation. For example:
java@Command(exitCodeOnInvalidInput = 123,
exitCodeOnExecutionException = 456)
Additionally, applications can configure a IExitCodeExceptionMapper
to map a specific exception to an exit code:
javaclass MyMapper implements IExitCodeExceptionMapper {
@Override
public int getExitCode(Throwable t) {
if (t instanceof FileNotFoundException) {
return 74;
}
return 1;
}
}
When the end user specified invalid input, the execute
method prints an error message followed by the usage help message of the command, and returns an exit code. This can be customized by configuring an IParameterExceptionHandler
.
If the business logic of the command throws an exception, the execute
method prints the stack trace of the exception and returns an exit code. This can be customized by configuring an IExecutionExceptionHandler
.
9.4. Usage Help Exit Code Section
By default, the usage help message does not include exit code information.
Applications that call System.exit
need to configure the usage help message to show exit code details,
either with the exitCodeListHeading
and exitCodeList
annotation attributes,
or programmatically by calling UsageMessageSpec.exitCodeListHeading
and UsageMessageSpec.exitCodeList
.
See Exit Code List for details.
9.5. Execution Configuration
The following methods can be used to configure the behaviour of the execute
method:
-
get/setOut
-
get/setErr
-
get/setColorScheme - see Color Scheme example
-
get/setExecutionStrategy - see Initialization Before Execution example
-
get/setParameterExceptionHandler - see Invalid User Input example
-
get/setExecutionExceptionHandler - see Business Logic Exceptions example
-
get/setExitCodeExceptionMapper
|
The above methods are not applicable with (and ignored by) other entry points like parse , parseArgs , populateCommand , run , call , invoke , parseWithHandler and parseWithHandlers .
|
9.6. Migration
Older versions of picocli supported run
, call
, invoke
and parseWithHandlers
convenience methods that were similar to execute
but had limited support for parser configuration and limited support for exit codes.
These methods are deprecated from picocli 4.0.
The sections below show some common usages and how the same can be achieved with the execute
API.
9.6.1. Customizing Output Streams and ANSI settings
Before:
javaPrintStream out = // output stream for user-requested help
PrintStream err = // output stream for error messages
Ansi ansi = // to use ANSI colors and styles or not
CommandLine.run(new MyRunnable(), out, err, ansi, args);
After:
javaPrintWriter out = // output stream for user-requested help
PrintWriter err = // output stream for error messages
Ansi ansi = // to use ANSI colors and styles or not
CommandLine cmd = new CommandLine(new MyRunnable())
.setOut(out);
.setErr(err);
.setColorScheme(Help.defaultColorScheme(ansi));
int exitCode = cmd.execute(args);
9.6.2. Return Value from Callable or Method
Before:
javaclass MyCallable implements Callable<MyResult> {
public MyResult call() { /* ... */ }
}
MyResult result = CommandLine.call(new MyCallable(), args);
After:
javaCommandLine cmd = new CommandLine(new MyCallable());
int exitCode = cmd.execute(args);
MyResult result = cmd.getExecutionResult();
9.6.3. Invoking Command Methods
Before:
javaclass MyCommand {
@Command
public MyResult doit(@Option(names = "-x") int x) { ... }
}
MyResult result = CommandLine.invoke("doit", MyCommand.class, args);
After:
javaMethod doit = CommandLine.getCommandMethods(MyCommand.class, "doit").get(0);
CommandLine cmd = new CommandLine(doit);
int exitCode = cmd.execute(args);
MyResult result = cmd.getExecutionResult();
9.6.4. Executing Commands with Subcommands
The IParseResultHandler2
interface has been deprecated in picocli 4.0 in favor of IExecutionStrategy
. The existing built-in handlers RunLast
, RunAll
and RunFirst
implement the IExecutionStrategy
interface and can still be used:
-
the
RunLast
handler prints help if requested, and otherwise gets the last specified command or subcommand and tries to execute it as aRunnable
,Callable
orMethod
. This is the default execution strategy. -
the
RunFirst
handler prints help if requested, and otherwise executes the top-level command as aRunnable
,Callable
orMethod
-
the
RunAll
handler prints help if requested, and otherwise executes all commands and subcommands that the user specified on the command line asRunnable
,Callable
orMethod
tasks
Before
javaCommandLine cmd = new CommandLine(MyTopLevelCommand())
.addSubcommand("status", new GitStatus())
.addSubcommand("commit", new GitCommit())
.addSubcommand("add", new GitAdd());
List<Object> result = cmd.parseWithHandler(new RunAll(), args);
After
javaCommandLine cmd = new CommandLine(MyTopLevelCommand())
.addSubcommand("status", new GitStatus())
.addSubcommand("commit", new GitCommit())
.addSubcommand("add", new GitAdd());
// the default is RunLast, this can be customized:
cmd.setExecutionStrategy(new RunAll());
int exitCode = cmd.execute(args);
The ParseResult
can be used to get the return value from a Callable or Method subcommand:
java// getting return value from Callable or Method command
Object topResult = cmd.getExecutionResult();
// getting return value from Callable or Method subcommand
ParseResult parseResult = cmd.getParseResult();
if (parseResult.subcommand() != null) {
CommandLine sub = parseResult.subcommand().commandSpec().commandLine();
Object subResult = sub.getExecutionResult();
}
9.7. DIY Command Execution
Alternatively, applications may want to use the parseArgs
method directly and write their own "Do It Yourself" command execution logic.
The example below covers the following common scenarios:
-
Handle invalid user input, and report any problems to the user (potentially suggesting alternative options and subcommands for simple typos if we want to get fancy).
-
Check if the user requested usage help, and print this help and abort processing if this was the case.
-
Check if the user requested version information, and print this information and abort processing if this was the case.
-
If none of the above, run the business logic of the application.
-
Handle any errors that occurred in the business logic.
javaCallable<Object> callable = new MyCallable();
CommandLine cmd = new CommandLine(callable);
try {
ParseResult parseResult = cmd.parseArgs(args);
// Did user request usage help (--help)?
if (cmd.isUsageHelpRequested()) {
cmd.usage(cmd.getOut());
return cmd.getCommandSpec().exitCodeOnUsageHelp();
// Did user request version help (--version)?
} else if (cmd.isVersionHelpRequested()) {
cmd.printVersionHelp(cmd.getOut());
return cmd.getCommandSpec().exitCodeOnVersionHelp();
}
// invoke the business logic
Object result = callable.call();
cmd.setExecutionResult(result);
return cmd.getCommandSpec().exitCodeOnSuccess();
// invalid user input: print error message and usage help
} catch (ParameterException ex) {
cmd.getErr().println(ex.getMessage());
if (!UnmatchedArgumentException.printSuggestions(ex, cmd.getErr())) {
ex.getCommandLine().usage(cmd.getErr());
}
return cmd.getCommandSpec().exitCodeOnInvalidInput();
// exception occurred in business logic
} catch (Exception ex) {
ex.printStackTrace(cmd.getErr());
return cmd.getCommandSpec().exitCodeOnExecutionException();
}
The CommandLine.execute
method is equivalent to the above, and additionally handles subcommands correctly.
9.8. Handling Errors
Internally, the execute
method parses the specified user input and populates the options and positional parameters defined by the annotations.
When the user specified invalid input, this is handled by the IParameterExceptionHandler
.
After parsing the user input, the business logic of the command is invoked: the run
, call
or @Command
-annotated method.
When an exception is thrown by the business logic, this is handled by the IExecutionExceptionHandler
.
In most cases, the default handlers are sufficient, but the sections below show how they can be customized.
9.8.1. Invalid User Input
When the user specified invalid input, the parser throws a ParameterException
.
In the execute
method, such exceptions are caught and passed to the IParameterExceptionHandler
.
The default parameter exception handler prints an error message describing the problem, followed by either suggested alternativeshttps://picocli.info/apidocs-all/info.picocli/picocli/CommandLine.UnmatchedArgumentException.html#printSuggestions(picocli.CommandLine.ParameterException,java.io.PrintWriter) for mistyped options, or the full usage help message of the problematic command. Finally, the handler returns an exit code. This is sufficient for most applications.
Sometimes you want to display a shorter message. For example, the grep
utility does not show the full usage help when it gets an invalid argument:
$ grep -d recurese "ERROR" logs/* Error: invalid argument ‘recurese’ for ‘--directories’ Valid arguments are: - ‘read’ - ‘recurse’ - ‘skip’ Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information.
You can customize how your application handles invalid user input by setting a custom IParameterExceptionHandler
:
javanew CommandLine(new MyApp())
.setParameterExceptionHandler(new ShortErrorMessageHandler())
.execute(args);
Where the IParameterExceptionHandler
implementation could be something like this:
javaclass ShortErrorMessageHandler implements IParameterExceptionHandler {
public int handleParseException(ParameterException ex, String[] args) {
CommandLine cmd = ex.getCommandLine();
PrintWriter err = cmd.getErr();
// if tracing at DEBUG level, show the location of the issue
if ("DEBUG".equalsIgnoreCase(System.getProperty("picocli.trace"))) {
err.println(cmd.getColorScheme().stackTraceText(ex));
}
err.println(cmd.getColorScheme().errorText(ex.getMessage())); // bold red
UnmatchedArgumentException.printSuggestions(ex, err);
err.print(cmd.getHelp().fullSynopsis());
CommandSpec spec = cmd.getCommandSpec();
err.printf("Try '%s --help' for more information.%n", spec.qualifiedName());
return cmd.getExitCodeExceptionMapper() != null
? cmd.getExitCodeExceptionMapper().getExitCode(ex)
: spec.exitCodeOnInvalidInput();
}
}
9.8.2. Business Logic Exceptions
When the business logic throws an exception, this exception is caught and passed to the IExecutionExceptionHandler
.
The default execution exception handling results in the stack trace of the exception being printed and an exit code being returned. This is sufficient for most applications.
If you have designed your business logic to throw exceptions with user-facing error messages, you want to print this error message instead of the stack trace.
This can be accomplished by installing a custom IExecutionExceptionHandler
, like this:
javanew CommandLine(new MyApp())
.setExecutionExceptionHandler(new PrintExceptionMessageHandler())
.execute(args);
Where the IExecutionExceptionHandler
implementation could look something like this:
javaclass PrintExceptionMessageHandler implements IExecutionExceptionHandler {
public int handleExecutionException(Exception ex,
CommandLine cmd,
ParseResult parseResult) {
// bold red error message
cmd.getErr().println(cmd.getColorScheme().errorText(ex.getMessage()));
return cmd.getExitCodeExceptionMapper() != null
? cmd.getExitCodeExceptionMapper().getExitCode(ex)
: cmd.getCommandSpec().exitCodeOnExecutionException();
}
}
9.9. Rare Use Cases
The CommandLine::execute
method is the recommended way to execute your command line application, as it provides configurable exception handling, handles user requests for usage help or version information, results in short and simple application code, and never throws an exception.
However, there may be use cases for which the execute
method is not a good match.
The alternative is to use CommandLine::parseArgs
and handle the resulting ParseResult
object in your application.
The DIY Command Execution section shows what is involved in doing so.
The parseArgs
method may be useful when writing parser test code, or when your application’s main
method is called by another application. The following sections go into some detail.
9.9.1. Parser Test Code Example
The parseArgs
method is useful in test code that only exercises the parsing logic, without involving the business logic.
For example:
javaMyApp app = new MyApp();
new CommandLine(app).parseArgs("--some --options and parameters".split(" "));
assertTrue(app.some);
9.9.2. Percolating Exceptions Up
The execute
method never throws an exception, and for some applications this is undesirable.
The parseArgs
method can also be useful when the main
method of your application is called by another application, and this other application is responsible for error handling.
An common use case is when your application is called as part of the build.
For example, Maven provides the exec-maven-plugin
https://www.mojohaus.org/exec-maven-plugin/ with exec:java
goalhttps://www.mojohaus.org/exec-maven-plugin/java-mojo.html, and Gradle similarly provides the Exechttps://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html and JavaExechttps://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html tasks.
The Maven exec:java
goal invokes the target class in the same Maven process.
In this case, we don’t want to call System.exit
, because it would stop the entire Maven process, and additionally, we want the exceptions thrown by the command line application to be handled by Maven.
One idea is to provide a separate main
method that uses parseArgs
instead of execute
.
For example:
javapublic class MyApp implements Callable {
/** Calls System.exit when called from the command line. */
public static void main(String... args) throws Exception {
System.exit(new CommandLine(new MyApp()).execute(args));
}
/**
* Nested helper class that can be used safely from the build tool:
* it does not call System.exit and it percolates exceptions up
* for handling by the caller.
*/
public static class NoSystemExit {
public static void main(String... args) throws Exception {
MyApp app = new MyApp();
ParseResult pr = new CommandLine(app).parseArgs(args);
if (CommandLine.executeHelpRequest(pr) != null) { return; } // help was requested
app.call(); // execute business logic, which may also throw an exception
}
}
//...
Then, in the build configuration, invoke nested class MyApp.NoSystemExit
instead of MyApp
to let the build tool handle any exceptions and avoid calling System.exit
.
9.9.3. System.exit or not?
An alternative to the above solution is to decide at runtime whether to call System.exit
or not.
The example implementation below demonstrates how to use system properties to determine whether to call System.exit
or not:
javapublic static void main(String... args) {
int exitCode = new CommandLine(new App()).execute(args);
if ((exitCode == CommandLine.ExitCode.OK && exitOnSuccess())
|| (exitCode != CommandLine.ExitCode.OK && exitOnError())) {
System.exit(exitCode);
}
}
private static boolean exitOnSuccess() {
return syspropDefinedAndNotFalse("systemExitOnSuccess");
}
private static boolean exitOnError() {
return syspropDefinedAndNotFalse("systemExitOnError");
}
private static boolean syspropDefinedAndNotFalse(String key) {
String value = System.getProperty(key);
return value != null && !"false".equalsIgnoreCase(value);
}
Picocli’s own Bash and ZSH completion script generator tool uses this method:
when this tool is called with a specific system property (picocli.autocomplete.systemExitOnError
) it will call System.exit
when an error occurs.
10. Validation
10.1. Built-in Validation
Picocli provides some limited form of validation:
-
options with one or more required parameters
-
allow/disallow single-value options being specified multiple times
-
mutually dependent options
-
mutually exclusive options
-
repeating groups of exclusive or dependent options
10.2. Custom Validation
Most applications will need to do additional validations to verify some business rules.
Applications that use the execute
API may find it useful to throw a ParameterException
when validation fails:
any ParameterExceptions
will be caught and handled by picocli’s built-in error handler, which shows the error message in bold red, and is followed by the usage help message.
To construct a ParameterException
, you need the CommandLine
instance where the error occurred.
This can be obtained from the CommandSpec
, which in turn can be obtained from a @Spec
-annotated field.
The sections below show some examples.
10.2.1. Single Value Validation
Methods annotated with @Option
and @Parameters
can do simple input validation by throwing a ParameterException
when invalid values are specified on the command line.
The following example validates that the value specified for the --prime
option is a prime number:
javaclass SingleOptionValidationExample {
private int prime;
@Spec CommandSpec spec; // injected by picocli
@Option(names = {"-p", "--prime"}, paramLabel = "NUMBER")
public void setPrimeNumber(int value) {
boolean invalid = false;
for (int i = 2; i <= value / 2; i++) {
if (value % i == 0) {
invalid = true;
break;
}
}
if (invalid) {
throw new ParameterException(spec.commandLine(),
String.format("Invalid value '%s' for option '--prime': " +
"value is not a prime number.", value));
}
prime = value;
}
// ...
}
10.2.2. Validating Option Combinations
Another common scenario is that the combination of multiple options and positional parameters is valid. One way to accomplish this is to perform such validation at the beginning of the business logic.
The following example validates that at least one of the --xml
, --csv
, or --json
options is specified:
java@Command(name = "myapp", mixinStandardHelpOptions = true, version = "myapp 0.1")
class MultiOptionValidationExample implements Runnable {
@Option(names="--xml") List<File> xmlFiles;
@Option(names="--csv") List<File> csvFiles;
@Option(names="--json") List<File> jsonFiles;
@Spec CommandSpec spec; // injected by picocli
public static void main(String... args) {
System.exit(new CommandLine(new MultiOptionValidationExample()).execute(args));
}
public void run() {
validate();
// remaining business logic here
}
private void validate() {
if (missing(xmlFiles) && missing(csvFiles) && missing(jsonFiles)) {
throw new ParameterException(spec.commandLine(),
"Missing option: at least one of the " +
"'--xml', '--csv', or '--json' options must be specified.");
}
}
private boolean missing(List<?> list) {
return list == null || list.isEmpty();
}
}
10.2.3. JSR-380 BeanValidation
If you want to keep your validation declarative and annotation-based, take a look at JSR 380https://jcp.org/en/jsr/detail?id=380.
JSR-380 is a specification of the Java API for bean validation, part of JavaEE and JavaSE, which ensures that the properties of a bean meet specific criteria, using annotations such as @NotNull
, @Min
, and @Max
.
The picocli wiki has a full examplehttps://github.com/remkop/picocli/wiki/JSR-380-BeanValidation, below is a snippet:
javaimport picocli.CommandLine;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.*;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.constraints.*;
import java.util.Set;
// Example inspired by https://www.baeldung.com/javax-validation
public class User implements Runnable {
@NotNull(message = "Name cannot be null")
@Option(names = {"-n", "--name"}, description = "mandatory")
private String name;
@Min(value = 18, message = "Age should not be less than 18")
@Max(value = 150, message = "Age should not be greater than 150")
@Option(names = {"-a", "--age"}, description = "between 18-150")
private int age;
@Email(message = "Email should be valid")
@Option(names = {"-e", "--email"}, description = "valid email")
private String email;
@Spec CommandSpec spec;
public User() { }
@Override
public String toString() {
return String.format("User{name='%s', age=%s, email='%s'}", name, age, email);
}
public static void main(String... args) {
new CommandLine(new User()).execute(args);
}
@Override
public void run() {
validate();
// remaining business logic here
}
private void validate() {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(this);
if (!violations.isEmpty()) {
String errorMsg = "";
for (ConstraintViolation<User> violation : violations) {
errorMsg += "ERROR: " + violation.getMessage() + "n";
}
throw new ParameterException(spec.commandLine(), errorMsg);
}
}
}
10.2.4. Using a Custom Execution Strategy for Validation
The above JSR-380 BeanValidation can also be accomplished with a custom IExecutionStrategy
that does the validation before executing the command.
This moves the validation logic into a separate class.
Picocli invokes this logic, removing the need to call a validate
method from the business logic.
Such a custom execution strategy could look like this:
javaclass ValidatingExecutionStrategy implements IExecutionStrategy {
public int execute(ParseResult parseResult) {
validate(parseResult.commandSpec());
return new CommandLine.RunLast().execute(parseResult); // default execution strategy
}
void validate(CommandSpec spec) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Object>> violations = validator.validate(spec.userObject());
if (!violations.isEmpty()) {
String errorMsg = "";
for (ConstraintViolation<?> violation : violations) {
errorMsg += "ERROR: " + violation.getMessage() + "n";
}
throw new ParameterException(spec.commandLine(), errorMsg);
}
}
}
The application can wire in this custom execution strategy as follows:
java public static void main(String... args) {
new CommandLine(new MyApp())
.setExecutionStrategy(new ValidatingExecutionStrategy())
.execute(args);
}
11. Parser Configuration
11.1. Case Sensitivity
By default, all options and subcommands are case sensitive. From picocli 4.3, case sensitivity is configurable. Case sensitivity can be switched off globally, as well as on a per-command basis.
To toggle case sensitivity for all commands, use the CommandLine::setSubcommandsCaseInsensitive
and CommandLine::setOptionsCaseInsensitive
methods.
Use the CommandSpec::subcommandsCaseInsensitive
and CommandSpec::optionsCaseInsensitive
methods to give some commands a different case sensitivity than others.
Where possible, picocli will try to prevent ambiguity: when multiple options with the same name are registered in a command, a DuplicateOptionAnnotationsException
is thrown.
When multiple subcommands with the same name are registered in a command, a DuplicateNameException
is thrown.
With case sensitivity switched off, the same principle applies: multiple options whose names differ only in case cannot be registered in a command. Similarly, multiple subcommands cannot be registered when their names differ only in case.
|
When a combination of POSIX options resembles a long option, picocli will prioritize the long option.
This is the case regardless of case sensitivity, but be aware that with case sensitivity switched off, the chance of such collisions increases.
For example, if a command has POSIX options |
See the casesensitivityhttps://github.com/remkop/picocli/tree/main/picocli-examples/src/main/java/picocli/examples/casesensitivity package in picocli-examples
for some examples.
11.2. Abbreviated Options and Subcommands
Since picocli 4.4, the parser can recognize abbreviated options and subcommands.
This needs to be enabled explicitly with CommandLine::setAbbreviatedOptionsAllowed
and CommandLine::setAbbreviatedSubcommandsAllowed
.
11.2.1. Recognized Abbreviations
When abbreviations are enabled, users can specify the initial letter(s) of the first component and optionally of one or more subsequent components of an option or subcommand name.
"Components" are separated by -
dash characters or by case, so for example, both --CamelCase
and --kebab-case
have two components.
|
When case sensitivity is disabled only the |
Option or Subcommand | Sample Recognized Abbreviations |
---|---|
|
|
|
|
|
|
11.2.2. Ambiguous Abbreviations
When the user specifies input that can match multiple options or subcommands, the parser throws a ParameterException
.
When applications use the execute
method, an error message and the usage help is displayed to the user.
For example, given a command with subcommands help
and hello
, then ambiguous user input like hel
will show this error message:
Error: 'hel' is not unique: it matches 'hello', 'help'
11.2.3. Abbreviated Long Options and POSIX Clustered Short Options
When an argument can match both an abbreviated long option and a set of clustered short options, picocli matches the long option. This is the case regardless of abbreviations, but be aware that with abbreviated options enabled, the chance of such collisions increases.
For example:
javaclass AbbreviationsAndPosix {
@Option(names = "-A") boolean a;
@Option(names = "-B") boolean b;
@Option(names = "-AaaBbb") boolean aaaBbb;
}
AbbreviationsAndPosix app = new AbbreviationsAndPosix();
new CommandLine(app).setAbbreviatedOptionsAllowed(true).parseArgs("-AB");
assert app.aaaBbb == true; // the long option is matched from abbreviated input -AB
assert app.a == false;
assert app.b == false;
When abbreviated options are enabled, user input -AB
will match the long -AaaBbb
option, but not the -A
and -B
options.
11.3. Overwriting Single Options
When a single-value option is specified multiple times on the command line, the default parser behaviour is to throw an OverwrittenOptionException
.
For example:
java@Option(names = "-p") int port;
The following input results in an OverwrittenOptionException
:
<command> -p 80 -p 8080
Applications can change this by calling CommandLine::setOverwrittenOptionsAllowed
with true
before parsing the input.
When overwritten options are allowed, the last specified value takes effect (the above input will set the port
field to 8080
)
and a WARN level message is printed to the console. (See Tracing for how to switch off the warnings.)
11.4. Stop At Positional
By default, positional parameters can be mixed with options on the command line, but this is not always desirable.
From picocli 2.3, applications can call CommandLine::setStopAtPositional
with true
to force the parser to treat all values following the first positional parameter as positional parameters.
When this flag is set, the first positional parameter effectively serves as an "end of options" marker.
11.5. Stop At Unmatched
From picocli 2.3, applications can call CommandLine::setStopAtUnmatched
with true
to force the parser to stop interpreting
options and positional parameters as soon as it encounters an unmatched argument.
When this flag is set, the first unmatched argument and all subsequent command line arguments are added to the
unmatched arguments list returned by CommandLine::getUnmatchedArguments
.
11.6. Unmatched Input
By default, an UnmatchedArgumentException
is thrown when a command line argument cannot be assigned to an option or positional parameter.
For example:
javaclass OnlyThree {
@Parameters(arity = "3") String[] values;
}
The command has only one annotated field, values
, and it expects exactly three arguments,
so the following input results in an UnmatchedArgumentException
:
java OnlyThree 1 2 3 4 5
Applications can change this by calling CommandLine::setUnmatchedArgumentsAllowed
with true
before parsing the input.
When unmatched arguments are allowed, the above input will be accepted and a WARN level message is printed to the console.
(See Tracing for how to switch off the warnings.)
The unmatched argument values can be obtained with the CommandLine::getUnmatchedArguments
method.
@Unmatched
annotation
11.7. As of picocli 3.0, fields annotated with @Unmatched
will be populated with the unmatched arguments.
The field must be of type String[]
or List<String>
.
If picocli finds a field annotated with @Unmatched
, it automatically sets unmatchedArgumentsAllowed
to true
so no UnmatchedArgumentException
is thrown when a command line argument cannot be assigned to an option or positional parameter.
If no unmatched arguments are found, the value of the field annotated with @Unmatched
is unchanged.
11.8. Unknown Options
11.8.1. Unknown Options Definition
A special case of unmatched input are arguments that resemble options but don’t match any of the defined options. Picocli determines if a value "resembles an option" by comparing its leading characters to the prefix characters of the known options.
|
Negative numbers are not considered to be unknown options, so values like -123 , -NaN , -Infinity , -#ABC and -0xCAFEBABE will not be treated specially for resembling an option name.
|
For example, the value -z
is considered an unknown option when we have a command that only defines options -x
and -y
:
java@Option(names = "-x") String x;
@Option(names = "-y") String y;
@Parameters String[] remainder;
The above defines options -x
and -y
, but no option -z
. So what should the parser do when the user gives input like this?
<command> -z -x XXX
11.8.2. Positional Parameters Resembling Options
One possibility is to silently accept such values as positional parameters, but this is often not desirable.
By default, when the value resembles an option, picocli throws an UnmatchedArgumentException
rather than treating it as a positional parameter.
Picocli 3.0 introduced the CommandLine::setUnmatchedOptionsArePositionalParams
method that can be used to force the parser to treat arguments resembling an option as positional parameters.
For example:
<command> -z -x XXX
When unmatchedOptionsArePositionalParams
is set to true
, the unknown option -z
is treated as a positional parameter.
The next argument -x
is recognized and processed as a known option like you would expect.
|
An alternative is to call CommandLine::setUnmatchedArgumentsAllowed with true , this will accept and store such values separately as described in Unmatched Input.
|
11.8.3. Option Parameters Resembling Options
By default, options accept parameter values that "resemble" (but don’t exactly match) an option.
Picocli 4.4 introduced a CommandLine::setUnmatchedOptionsAllowedAsOptionParameters
method that makes it possible to configure the parser to reject values that resemble options as option parameters.
Setting this to false
will result in values resembling option names being rejected as option values.
For example:
javaclass MyApp {
@Option(names = "-x") String x;
}
By default, a value like -z
, which resembles an option, is accepted as the parameter for -x
:
javaMyApp app = new MyApp();
new CommandLine(app).parseArgs("-x", "-z");
assert "-z".equals(app.x);
After setting the unmatchedOptionsAllowedAsOptionParameters
parser option to false
, values resembling an option are rejected as parameter for -x
:
javanew CommandLine(new MyApp())
.setUnmatchedOptionsAllowedAsOptionParameters(false)
.parseArgs("-x", "-z");
This will throw an UnmatchedArgumentException
with message:
"Unknown option: '-z'; Expected parameter for option '-x' but found '-z'"
11.9. Option Names or Subcommands as Option Values
11.9.1. By Default Options Do Not Consume Option Names or Subcommands
Since picocli 4.4, the parser will no longer assign values that match a subcommand name or an option name to options that take a parameter, unless the value is in quotes. For example:
javaclass App {
@Option(names = "-x") String x;
@Option(names = "-y") String y;
public static void main(String... args) {
App app = new App();
new CommandLine(app).setTrimQuotes(true).parseArgs(args);
System.out.printf("x='%s', y='%s'%n", app.x, app.y);
}
}
In previous versions of picocli, the above command would accept input -x -y
, and the value -y
would be assigned to the x
String field.
As of picocli 4.4, the above input will be rejected with an error message indicating that the -x
option requires a parameter.
If it is necessary to accept values that match option names, such values need to be quoted. For example:
bashjava App -x="-y"
This will print the following output:
bashx='-y', y='null'
11.9.2. Enable Consuming Option Names or Subcommands
Picocli 4.7.0 introduces two parser configuration options to change this behaviour:
-
CommandLine::setAllowOptionsAsOptionParameters
allows options to consume option names -
CommandLine::setAllowSubcommandsAsOptionParameters
allows options to consume subcommand names
When set to true
, all options in the command (options that take a parameter) can consume values that match option names or subcommand names.
This means that any option will consume the maximum number of arguments possible for its arity.
|
USE WITH CAUTION! If an option is defined as |
11.9.3. Custom Parsing for Option-Specific Behaviour
The parser configuration options in the previous section apply to all options in the command.
Some applications may want to enable options consuming option names or subcommands for some options, but not for all options in the command. Such applications can replace or augment picocli’s parser by doing custom parameter processing for such options. For example:
javaclass App implements Runnable {
@Option(names = "-x", parameterConsumer = App.CustomConsumer.class)
String x;
@Option(names = "-y")
String y;
@Command
public void mySubcommand() {}
static class CustomConsumer implements IParameterConsumer {
@Override
public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec cmdSpec) {
if (args.isEmpty()) {
throw new ParameterException(cmdSpec.commandLine(),
"Error: option '-x' requires a parameter");
}
String arg = args.pop();
argSpec.setValue(arg);
}
}
public void run() {
System.out.printf("x='%s', y='%s'%n", x, y);
}
public static void main(String... args) {
new CommandLine(new App()).execute(args);
}
}
The above code assigns whatever command line argument that follows the -x
option to that option,
and allows input like the following:
bashjava App -x=mySubcommand
java App -x mySubcommand
java App -x=-y
java App -x -y -y=123
11.10. Toggle Boolean Flags
When a flag option is specified on the command line picocli will set its value to the opposite of its default value.
Prior to 4.0, the default was to "toggle" boolean flags to the opposite of their current value:
if the previous value was true
it is set to false
, and when the value was false
it is set to true
.
Applications can call CommandLine::setToggleBooleanFlags
with true
to enable toggling.
Note that when toggling is enabled, specifying a flag option twice on the command line will have no effect because they cancel each other out.
11.11. POSIX Clustered Short Options
By default, the picocli parser allows POSIX clustered short options, so short options like -x -v -f SomeFile
can be clustered together like -xvfSomeFile
.
From picocli 3.0, applications can call CommandLine::setPosixClusteredShortOptionsAllowed
with false
to enforce that options must be separated with whitespace on the command line.
(This also means that option parameters must be separated from the option name by whitespace or the =
separator character, so -D key=value
and -D=key=value
will be recognized but -Dkey=value
will not.)
11.12. Lenient Mode
From picocli 3.2, the parser can be configured to continue parsing invalid input to the end.
When collectErrors
is set to true
, and a problem occurs during parsing, an Exception
is added to the list returned by ParseResult::errors
and parsing continues. The default behaviour (when collectErrors
is false
) is to abort parsing by throwing the Exception
.
This is useful when generating completion candidates on partial input, and is also useful when using picocli in languages like Clojure where idiomatic error handling does not involve throwing and catching exceptions.
When using this feature, applications are responsible for actively verifying that no errors occurred before executing the business logic. Use with care!
11.13. Quoted Values
11.13.1. Trimming Quotes
From picocli 3.7, quotes around command line parameters are preserved by default (previously they were removed). This can be configured with CommandLine::setTrimQuotes
, or the parser configuration trimQuotes
. From picocli 4.0, quoted arguments can contain nested quoted substrings, to give end users fine-grained control over how values are split.
If CommandLine::setTrimQuotes
, or the parser configuration trimQuotes
is set to true
, picocli will remove quotes from the command line arguments, as follows:
-
As each command line argument is processed, the below smart unquote procedure is used to trim the outermost quotes.
-
Next, if the option or positional parameter has a
split
regex defined, the parameter value is split while respecting quotes: thesplit
regex is not matched if it occurs in a quoted substring of the parameter value. Each of the parts found by the splitting process will have its quotes removed using the below "smart unquote" procedure.
See the Splitting Quoted Parameters section below for examples.
Smart Unquote
-
If the command line argument contains just the leading and trailing quote, these quotes are removed.
-
If the command line argument contains unescaped quotes, other than the leading and trailing quote, the argument is unchanged (the leading and trailing quotes remain).
-
If a quoted command line argument contains backslash-escaped quotes, the leading and trailing quotes are removed, backslash-escaped quotes are converted to unescaped quotes, and backslash-escaped backslashes are converted to unescaped backslashes.
For example:
Command Line Argument | After Trimming Quotes | Note |
---|---|---|
|
|
quotes removed |
|
|
left unchanged |
|
|
Splitting will find 4 values: |
|
|
Splitting will find 1 value: |
11.13.2. Splitting Quoted Parameters
By default, if the option or positional parameter has a split
regex defined, parameter values are split into parts while respecting quotes: the split
regular expression is not matched inside a quoted region.
Example:
java@Option(names = "-x", split = ",")
String[] parts;
Given input like below:
<command> -x "-Dvalues=a,b,c","-Dother=1,2"
This results in the parts
array having the following values, assuming the parser configuration trimQuotes
is false
(the default):
"-Dvalues=a,b,c"
"-Dother=1,2"
If the parser configuration trimQuotes
is true
, the above example would be split into the following values (with quotes trimmed from the resulting parts):
-Dvalues=a,b,c
-Dother=1,2
Given input like below:
<command> -x a,b,"c,d,e",f,"xxx,yyy"
This results in the parts
array having the following values:
a
b
"c,d,e"
f
"xxx,yyy"
Or, if the parser configuration trimQuotes
is true
:
a
b
c,d,e
f
xxx,yyy
To preserve quotes when trimQuotes
is true
, specify additional nested quotes on the command line. For example:
<command> "-x="a,b,"c,d,e",f"" "x,y,z" ""1,2,3"" ""1,2,3""
With parser configuration trimQuotes
set to true
, the above input gives the following values:
a,b,"c,d,e",f
x
y
z
1,2,3
"1,2,3"
This "smart splitting" (respecting quotes) can be switched off with CommandLine::setSplitQuotedStrings
:
setting the splitQuotedStrings
parser attribute to true
switches off smart splitting,
and the split
regex is applied to the parameter value regardless of quotes.
|
|
11.14. Customizing Negatable Options
Negatable options can be customized via the INegatableOptionTransformer
interface:
javainterface INegatableOptionTransformer {
/**
* Returns the negative form of the specified option name for the parser to recognize
* when parsing command line arguments.
* @param optionName the option name to create a negative form for,
* for example {@code --force}
* @param cmd the command that the option is part of
* @return the negative form of the specified option name, for example {@code --no-force}
*/
String makeNegative(String optionName, CommandSpec cmd);
/**
* Returns the documentation string to show in the synopsis and usage help message for
* the specified option. The returned value should be concise and clearly suggest that
* both the positive and the negative form are valid option names.
* @param optionName the option name to create a documentation string for,
* for example {@code --force}, or {@code -XX:+<option>}
* @param cmd the command that the option is part of
* @return the documentation string for the negatable option,
* for example {@code --[no-]force}, or {@code -XX:(+|-)<option>}
*/
String makeSynopsis(String optionName, CommandSpec cmd);
}
This allows you to control:
-
which option names should have a negative form
-
the negative form recognized by the parser while parsing the command line
-
the documentation string showing both the positive and the negative form in the usage help message
By default, a set of regular expressionshttps://picocli.info/apidocs-all/info.picocli/picocli/CommandLine.RegexTransformer.html#createDefault() is used to control the above.
Use CommandLine::setNegatableOptionTransformer
to replace the INegatableOptionTransformer
https://picocli.info/apidocs-all/info.picocli/picocli/CommandLine.INegatableOptionTransformer with a custom version.
See the JavaDoc for details.
11.15. Custom Parameter Processing
As of version 4.6, picocli offers two different parser plugins for custom parameter processing: while the IParameterPreprocessor
is a powerful and flexible tool, the IParameterConsumer
serves as a simpler alternative.
IParameterConsumer
Parser Plugin
11.15.1. Options or positional parameters can be assigned an IParameterConsumer
that implements
custom logic to process the parameters for this option or this position.
When an option or positional parameter with a custom IParameterConsumer
is matched on the command line, picocli’s internal parser is temporarily suspended,
and the custom parameter consumer becomes responsible for consuming and processing as many
command line arguments as needed.
This can be useful when passing options through to another command.
For example, the unix find
https://en.wikipedia.org/wiki/Find_(Unix) command
has a -exec
https://en.wikipedia.org/wiki/Find_(Unix)#Execute_an_action
option to execute some action for each file found.
Any arguments following the -exec
option until a ;
or +
argument are not
options for the find
command itself, but are interpreted as a separate command and its options.
The example below demonstrates how to implement find -exec
using this API:
java@Command(name = "find")
class Find {
@Option(names = "-exec", parameterConsumer = ExecParameterConsumer.class)
List<String> list = new ArrayList<String>();
}
class ExecParameterConsumer implements IParameterConsumer {
public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
List<String> list = argSpec.getValue();
while (!args.isEmpty()) {
String arg = args.pop();
list.add(arg);
// `find -exec` semantics: stop processing after a ';' or '+' argument
if (";".equals(arg) || "+".equals(arg)) {
break;
}
}
}
}
|
Make sure any nested classes are static , or picocli will not be able to instantiate them.
|
IParameterPreprocessor
Parser Plugin
11.15.2. Introduced in picocli 4.6, the IParameterPreprocessor
is also a parser plugin, similar to IParameterConsumer
, but more flexible.
Options, positional parameters and commands can be assigned an IParameterPreprocessor
that implements custom logic to preprocess the parameters for this option, position or command.
When an option, positional parameter or command with a custom IParameterPreprocessor
is matched on the command line, picocli’s internal parser is temporarily suspended, and this custom logic is invoked.
This custom logic may completely replace picocli’s internal parsing for this option, positional parameter or command, or augment it by doing some preprocessing before picocli’s internal parsing is resumed for this option, positional parameter or command.
The "preprocessing" actions can include modifying the stack of command line parameters, or modifying the model.
Example use case
This may be useful when disambiguating input for commands that have both a positional parameter and an option with an optional parameter. For example, suppose we have a command with the following synopsis:
edit [--open[=<editor>]] <file>
One of the limitations of options with an optional parameter is that they are difficult to combine with positional parameters.
With a custom parser plugin, we can customize the parser, such that VALUE
in --option=VALUE
is interpreted as the option parameter, and in --option VALUE
(without the =
separator), VALUE is interpreted as the positional parameter.
The code below demonstrates this:
java@Command(name = "edit")
class Edit {
@Parameters(index = "0", arity="0..1", description = "The file to edit.")
File file;
enum Editor { defaultEditor, eclipse, idea, netbeans }
@Option(names = "--open", arity = "0..1", preprocessor = Edit.MyPreprocessor.class,
description = {
"Optionally specify the editor to use (${COMPLETION-CANDIDATES}). " +
"If omitted the default editor is used. ",
"Example: edit --open=idea FILE opens IntelliJ IDEA (notice the '=' separator)",
" edit --open FILE opens the specified file in the default editor"
})
Editor editor = Editor.defaultEditor;
static class MyPreprocessor implements IParameterPreprocessor {
public boolean preprocess(Stack<String> args,
CommandSpec commandSpec,
ArgSpec argSpec,
Map<String, Object> info) {
// we need to decide whether the next arg is the file to edit
// or the name of the editor to use...
if (" ".equals(info.get("separator"))) { // parameter was not attached to option
// act as if the user specified --open=defaultEditor
args.push(Editor.defaultEditor.name());
}
return false; // picocli's internal parsing is resumed for this option
}
}
}
|
Make sure any nested classes are static , or picocli will not be able to instantiate them.
|
With this preprocessor in place the user can now specify his editor of choice (e.g. --open=idea
). If no editor is given, the default editor is used:
# User input # Command State # -------------------------- --open A B # editor: defaultEditor, file: A, unmatched: [B] --open A # editor: defaultEditor, file: A, unmatched: [] --open=A B # editor: A, file: B, unmatched: [] --open=A # editor: A, file: null, unmatched: []
11.15.3. Parser Plugin Comparison
IParameterPreprocessor |
IParameterConsumer |
|
---|---|---|
Summary |
Either augment (return |
Replaces picocli parsing logic for the matched element. |
Scope |
Commands as well as options and positional parameters. |
Options and positional parameters only. |
Read parser state |
Yes, information may be received via the |
No |
Modify parser state |
Yes, via modifying the |
No |
12. Help
12.1. Help Options
Applications can define help options by setting attribute versionHelp = true
, usageHelp = true
or help = true
.
If one of the arguments specified on the command line is a "help" option, picocli will not throw a MissingParameterException
when required options are missing.
For example:
java@Option(names = {"-V", "--version"}, versionHelp = true, description = "display version info")
boolean versionInfoRequested;
@Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message")
boolean usageHelpRequested;
Use these attributes for options that request the usage help message or version information to be shown on the console.
javaApp app = CommandLine.populateCommand(new App(), args);
if (app.usageHelpRequested) {
CommandLine.usage(new App(), System.out);
return;
}
The CommandLine
class offers two methods that allow external components to detect whether
usage help or version information was requested (without inspecting the annotated domain object):
-
CommandLine::isUsageHelpRequested
returnstrue
if the parser matched an option annotated withusageHelp=true
-
CommandLine::isVersionHelpRequested
returnstrue
if the parser matched an option annotated withversionHelp=true
javaCommandLine commandLine = new CommandLine(new App());
commandLine.parseArgs(args);
if (commandLine.isUsageHelpRequested()) {
commandLine.usage(System.out);
return;
} else if (commandLine.isVersionHelpRequested()) {
commandLine.printVersionHelp(System.out);
return;
}
// ... run App's business logic
See also Printing Help Automatically.
12.2. Mixin Standard Help Options
Picocli 3.0 introduced the mixinStandardHelpOptions
command attribute. When this attribute is set to true
, picocli adds a mixin to the
command that adds usageHelp
and versionHelp
options to the command. For example:
java@Command(mixinStandardHelpOptions = true, version = "auto help demo - picocli 3.0")
class AutoHelpDemo implements Runnable {
@Option(names = "--option", description = "Some option.")
String option;
@Override public void run() { /* ... */ }
}
Commands with mixinStandardHelpOptions
do not need to explicitly declare fields annotated with @Option(usageHelp = true)
and @Option(versionHelp = true)
any more.
The usage help message for the above example looks like this:
Usage: <main class> [-hV] [--option=<option>] --option=<option> Some option. -h, --help Show this help message and exit. -V, --version Print version information and exit.
12.3. Built-in Help Subcommand
As of version 3.0, picocli provides a help
subcommand (picocli.CommandLine.HelpCommand
) that can be installed as a subcommand
on any application command. It prints usage help for the parent command or sibling subcommands. For example:
javaimport picocli.CommandLine.HelpCommand;
@Command(name = "myapp", subcommands = {HelpCommand.class, Subcommand.class})
class MyCommand implements Runnable {
// ...
}
For example, the following command prints usage help for a subcommand:
bashmyapp help subcommand
To print usage help for the main command:
bashmyapp help
12.4. Custom Help Subcommands
Custom help subcommands should mark themselves as a help command to tell picocli not to throw a MissingParameterException
when required options are missing.
java@Command(helpCommand = true)
Picocli 4.0 introduced a new interface picocli.CommandLine.IHelpCommandInitializable2
that provides custom help
commands with access to the parent command and sibling commands, whether to use Ansi colors or not, and the streams to print the usage help message to.
The IHelpCommandInitializable2
interface replaces the IHelpCommandInitializable
interface which was introduced in picocli 3.0.
javapublic interface IHelpCommandInitializable2 {
/**
* Initializes this object with the information needed to implement a help command that
* provides usage help for other commands.
*
* @param helpCommandLine the {@code CommandLine} object associated with this help command.
* Implementors can use this to walk the command hierarchy and
* get access to the help command's parent and sibling commands.
* @param colorScheme the color scheme to use when printing help, including whether
* to use Ansi colors or not
* @param outWriter the output writer to print the usage help message to
* @param errWriter the error writer to print any diagnostic messages to,
* in addition to the output from the exception handler
*/
void init(CommandLine helpCommandLine,
Help.ColorScheme colorScheme,
PrintWriter outWriter,
PrintWriter errWriter);
}
12.5. Printing Help Automatically
As of picocli 2.0, the convenience methods will automatically print usage help and version information
when a help option was specified on the command line (options annotated with the versionHelp
or usageHelp
attribute - but not the help
attribute).
The same holds for the mixinStandardHelpOptions
attribute, the built-in HelpCommand
and any custom help subcommands marked as a help command.
The following convenience methods automatically print help:
-
CommandLine::execute
-
CommandLine::call
-
CommandLine::run
-
CommandLine::invoke
-
CommandLine::parseWithHandler
(with the built-inRun…
handlers) -
CommandLine::parseWithHandlers
(with the built-inRun…
handlers)
The following methods do not automatically print help:
-
CommandLine::parse
-
CommandLine::parseArgs
-
CommandLine::populateCommand
When using the last three methods, applications need to query the parse result to detect whether usage help or version help
was requested, and invoke CommandLine::usage
or CommandLine::printVersionHelp
to actually print the requested help message.
13. Version Help
13.1. Static Version Information
version
Attribute
13.1.1. Command As of picocli 0.9.8, applications can specify version information in the version
attribute of the @Command
annotation.
java@Command(version = "1.0")
class VersionedCommand {
@Option(names = { "-V", "--version" }, versionHelp = true,
description = "print version information and exit")
boolean versionRequested;
/* ... */ }
The CommandLine.printVersionHelp(PrintStream)
method extracts the version information from this
annotation and prints it to the specified PrintStream
.
javaCommandLine commandLine = new CommandLine(new VersionedCommand());
commandLine.parseArgs(args);
if (commandLine.isVersionHelpRequested()) {
commandLine.printVersionHelp(System.out);
return;
}
13.1.2. Multi-line Version Info
The version
may specify multiple Strings. Each will be printed on a separate line.
java@Command(version = { "Versioned Command 1.0", "Build 12345", "(c) 2017" })
class VersionedCommand { /* ... */ }
The CommandLine.printVersionHelp(PrintStream)
method will print the above as:
Versioned Command 1.0 Build 12345 (c) 2017
13.1.3. Version Info With Variables
As of picocli 4.0, the version strings may contain system properties and environment variables. For example:
java@Command(version = {
"Versioned Command 1.0",
"Picocli " + picocli.CommandLine.VERSION,
"JVM: ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})",
"OS: ${os.name} ${os.version} ${os.arch}"})
class VersionedCommand { /* ... */ }
Depending on your environment, that may print something like:
Versioned Command 1.0 Picocli 4.0.0 JVM: 1.8.0_202 (Oracle Corporation Substrate VM GraalVM 1.0.0-rc15 CE) OS: Linux 4.4.0-17134-Microsoft amd64
13.1.4. Version Info With Colors
The version strings may contain markup to show ANSI styles and colors. For example:
java@Command(version = {
"@|yellow Versioned Command 1.0|@",
"@|blue Build 12345|@",
"@|red,bg(white) (c) 2017|@" })
class VersionedCommand { /* ... */ }
The markup will be rendered as ANSI escape codes on supported systems.
bold black faint red underline green italic yellow blink blue reverse magenta reset cyan white Colors are applied as foreground colors by default.
You can set background colors by specifying The example below shows how this markup can be used to add colors and styles to the headings and descriptions of a usage help message: As of picocli 4.2, custom markup like
Pre-defined Styles
Pre-defined Colors
bg(<color>)
.
For example, @|bg(red) text with red background|@
.
Similarly, fg(<color>)
explicitly sets the foreground color.java@Command(name = "commit",
sortOptions = false,
headerHeading = "@|bold,underline Usage|@:%n%n",
synopsisHeading = "%n",
descriptionHeading = "%n@|bold,underline Description|@:%n%n",
parameterListHeading = "%n@|bold,underline Parameters|@:%n",
optionListHeading = "%n@|bold,underline Options|@:%n",
header = "Record changes to the repository.",
description = "Stores the current contents of the index in a new commit " +
"along with a log message from the user describing the changes.")
class GitCommit { /* ... */ }
Markup styles cannot be nested, for example:
@|bold this @|underline that|@|@
will not work. You can achieve the same by combining styles, for example: @|bold this|@ @|bold,underline that|@
will work fine.
@|bold mytext|@
, @|italic mytext|@
etc. can also be converted to custom markup like <b>mytext</b>
and <i>mytext</i>
in HTML, or *mytext*
and _mytext_
in lightweight markup languages like AsciiDoc.
Applications can control this by setting a ColorScheme
with a custom markup map.
This feature is used to generate man page documentation.
15.3. Styles and Colors in Application Output
The use of ANSI colors and styles is not limited to the usage help and version information.
Applications can use the picocli Ansi
class directly to create colored output.
By using the Ansi.AUTO
enum value, picocli will auto-detect whether it can emit ANSI escape codes or only the plain text.
javaimport picocli.CommandLine.Help.Ansi;
// ...
String str = Ansi.AUTO.string("@|bold,green,underline Hello, colored world!|@");
System.out.println(str);
15.4. More Colors
Most terminals support a 256 color indexed palettehttps://en.wikipedia.org/wiki/ANSI_escape_code#Colors:
0x00-0x07: standard colors (the named colors) 0x08-0x0F: high intensity colors (often similar to named colors + bold style) 0x10-0xE7: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) 0xE8-0xFF: grayscale from black to white in 24 steps
Colors from the 256 color palette can be specified by their index values or by their RGB components.
RGB components must be separated by a semicolon ;
and each component must be between 0
and 5
, inclusive.
For example, @|bg(0;5;0) text with red=0, green=5, blue=0 background|@
,
or @|fg(46) the same color by index, as foreground color|@
.
3.2 the description for an option or positional parameter replaced with the default value for that option or positional parameter 4.0 the description for an option with optional parameter replaced with the fallback value for that option or positional parameter 4.6 the description for map option or positional parameter that allows key-only parameters replaced with the map fallback value for that option or positional parameter 3.2 the description for an option or positional parameter replaced with the completion candidates for that option or positional parameter 4.0 any section of the usage help message for a command replaced with the name of the command 4.0 any section of the usage help message for a command replaced with the fully qualified name of the command (that is, preceded by its parent fully qualified name) 4.0 any section of the usage help message for a command replaced with the name of its parent command 4.0 any section of the usage help message for a command replaced with the fully qualified name of its parent command (that is, preceded by the name(s) of the parent command’s ancestor commands) 4.4 any section of the usage help message for a command replaced with the name of the top-level command (in a command suite with subcommands)
Variable
Since
Use in
Meaning
${DEFAULT-VALUE}
${FALLBACK-VALUE}
${MAP-FALLBACK-VALUE}
${COMPLETION-CANDIDATES}
${COMMAND-NAME}
${COMMAND-FULL-NAME}
${PARENT-COMMAND-NAME}
${PARENT-COMMAND-FULL-NAME}
${ROOT-COMMAND-NAME}
20.3. Custom Variables
In addition, you can define your own variables. Currently the following syntaxes are supported:
-
${sys:key}
: system property lookup, replaced by the value ofSystem.getProperty("key")
-
${env:key}
: environment variable lookup, replaced by the value ofSystem.getEnv("key")
-
${bundle:key}
: look up the value ofkey
in the resource bundle of the command -
${key}
: search all of the above, first system properties, then environment variables, and finally the resource bundle of the command
20.4. Default Values for Custom Variables
You can specify a default value to use when no value is found for a custom variable. The syntax for specifying a default is ${a:-b}
, where a
is the variable name and b
is the default value to use if a
is not found.
So, for the individual lookups, this looks like this:
${key:-defaultValue}
${sys:key:-defaultValue}
${env:key:-defaultValue}
${bundle:key:-defaultValue}
The default value may contain other custom variables. For example:
${bundle:a:-${env:b:-${sys:c:-X}}}
The above variable is expanded as follows. First, try to find key a
in the command’s resource bundle. If a
is not found in the resource bundle, get the value of environment variable b
. If no environment variable b
exists, get the value of system property c
. Finally, if no system property c
exists, the value of the expression becomes X
.
20.5. Escaping Variables
Sometimes you want to show a string like "${VAR}"
in a description.
A $
character can be escaped with another $
character. Therefore, $${VAR}
will not be interpreted as a VAR
variable, but will be replaced by ${VAR}
instead.
20.6. Switching Off Variable Interpolation
Variable interpolation can be switched off for the full command hierarchy by calling CommandLine.setInterpolateVariables(false)
, or for a particular command by calling CommandSpec.interpolateVariables(false)
.
20.7. Limitations of Variable Interpolation
Some attribute values need to be resolved early, when the model is constructed from the annotation values.
Specifically:
-
command names and aliases, option names, mixin names
-
arity
(for options and positional parameters) -
index
(for positional parameters) -
separator
(for commands)
It is possible for these attributes to contain variables, but be aware of the limitations.
If these attributes have variables, and the variables get a different value after the model is constructed, the change will not be reflected in the model.
21. Tips & Tricks
21.1. Programmatic API
Picocli also provides a programmatic API, in addition to the annotations API.
The programmatic API allows applications to dynamically create command line options on the fly, where not all options are known in advance. It also makes it possible to use picocli in other JVM languages that do not support annotations.
Another use case is creating idiomatic domain-specific languages for processing command line arguments. For example, Groovy’s CliBuilder DSLhttps://groovy-lang.org/dsls.html#_clibuilder is implemented using picocli’s programmatic API.
@Option
and @Parameters
Methods
21.2. As of version 3.2, @Option
and @Parameters
annotations can be added to methods as well as fields of a class.
For concrete classes, annotate "setter" methods (methods that accept a parameter) and when the option is specified on the command line, picocli will invoke the method with the value specified on the command line, converted to the type of the method parameter.
Alternatively, you may annotate "getter-like" methods (methods that return a value) on an interface, and picocli will create an instance of the interface that returns the values specified on the command line, converted to the method return type.
21.2.1. Annotating Methods of an Interface
The @Option
and @Parameters
annotations can be used on methods of an interface that return a value. For example:
javainterface Counter {
@Option(names = "--count")
int getCount();
}
You use it by specifying the class of the interface:
javaCommandLine cmd = new CommandLine(Counter.class); // specify a class
String[] args = new String[] {"--count", "3"};
cmd.parseArgs(args);
Counter counter = cmd.getCommand(); // picocli created an instance
assert counter.getCount() == 3; // method returns command line value
21.2.2. Annotating Methods of a Concrete Class
The @Option
and @Parameters
annotations can be used on methods of a class that accept a parameter. For example:
javaclass Counter {
int count;
@Option(names = "--count")
void setCount(int count) {
this.count = count;
}
}
You use it by passing an instance of the class:
javaCounter counter = new Counter(); // the instance to populate
CommandLine cmd = new CommandLine(counter);
String[] args = new String[] {"--count", "3"};
cmd.parseArgs(args);
assert counter.count == 3; // method was invoked with command line value
Methods annotated with @Option
and @Parameters
can do simple input validation by throwing a ParameterException
when invalid values are specified on the command line.
javaclass ValidationExample {
private Map<String, String> properties = new LinkedHashMap<>();
@Spec private CommandSpec spec; // injected by picocli
@Option(names = {"-D", "--property"}, paramLabel = "KEY=VALUE")
public void setProperty(Map<String, String> map) {
for (String key : map.keySet()) {
String newValue = map.get(key);
validateUnique(key, newValue);
properties.put(key, newValue);
}
}
private void validateUnique(String key, String newValue) {
String existing = properties.get(key);
if (existing != null && !existing.equals(newValue)) {
throw new ParameterException(spec.commandLine(),
String.format("Duplicate key '%s' for values '%s' and '%s'.",
key, existing, newValue));
}
}
// ...
}
|
Applications that reuse a single Annotated setter method options that explicitly set If the setter method annotation does not explicitly set Java Kotlin
|
@Command
Methods
21.3. As of picocli 3.6, methods can be annotated with @Command
.
The method parameters provide the command options and parameters. For example:
javaclass Cat {
public static void main(String[] args) {
Method doit = CommandLine.getCommandMethods(Cat.class, "cat").get(0);
CommandLine cmd = new CommandLine(doit);
int exitCode = cmd.execute(args);
}
@Command(description = "Concatenate FILE(s) to standard output.",
mixinStandardHelpOptions = true, version = "4.1.3")
int cat(@Option(names = {"-E", "--show-ends"}) boolean showEnds,
@Option(names = {"-n", "--number"}) boolean number,
@Option(names = {"-T", "--show-tabs"}) boolean showTabs,
@Option(names = {"-v", "--show-nonprinting"}) boolean showNonPrinting,
@Parameters(paramLabel = "FILE") File[] files) {
// process files
return CommandLine.ExitCode.OK;
}
}
The usage help of the above command looks like this:
Usage: cat [-EhnTvV] [FILE...] Concatenate FILE(s) to standard output. [FILE...] -E, --show-ends -h, --help Show this help message and exit. -n, --number -T, --show-tabs -v, --show-nonprinting -V, --version Print version information and exit.
See below for an example that uses a resource bundle to define usage help descriptions outside the code.
For positional parameters, the @Parameters
annotation may be omitted on method parameters.
|
If compiled with the -parameters flag on Java 8 or higher, the paramLabel of positional parameters is obtained from the method parameter name using reflection instead of the generic arg0, arg1, etc.
|
21.3.1. Subcommand Methods
If the enclosing class is annotated with @Command
, method commands are automatically added as subcommands to the class command, unless the class command has attribute @Command(addMethodSubcommands = false)
.
For example:
java@Command(name = "git", mixinStandardHelpOptions = true, version = "picocli-4.1.3",
resourceBundle = "Git_Messages", subcommands = { HelpCommand.class })
class Git {
@Option(names = "--git-dir", descriptionKey = "GITDIR")
Path path;
@Command
void commit(@Option(names = {"-m", "--message"}) String commitMessage,
@Option(names = "--squash", paramLabel = "<commit>") String squash,
@Parameters(paramLabel = "<file>") File[] files) {
// ... implement business logic
}
}
Use @Command(addMethodSubcommands = false)
on the class @Command
annotation if the @Command
-annotated methods in this class should not be added as subcommands.
21.3.2. Description Text in ResourceBundle
The usage help of the above git commit
example command is very minimal:
Usage: git commit [--squash=<commit>] [-m=<arg0>] [<file>...] [<file>...] --squash=<commit> -m, --message=<arg0>
You can use a resource bundle to move the descriptions out of the code:
# shared between all commands help = Show this help message and exit. version = Print version information and exit. # command-specific strings git.usage.description = Version control system git.GITDIR = Set the path to the repository git.commit.usage.description = Record changes to the repository git.commit.message = Use the given <msg> as the commit message. git.commit.squash = Construct a commit message for use with rebase --autosquash. git.commit.<file>[0..*] = The files to commit.
With this resource bundle, the usage help for the git commit
command looks like this:
Usage: git commit [--squash=<commit>] [-m=<arg0>] [<file>...] Record changes to the repository [<file>...] The files to commit. --squash=<commit> Construct a commit message for use with rebase --autosquash. -m, --message=<arg0> Use the given <msg> as the commit message.
@Command
Methods
21.3.3. Mixin Support in As of picocli 3.8, @Command
methods accept @Mixin
parameters.
All options and positional parameters defined in the mixin class are added to the command.
Example:
javaclass CommonParams {
@Option(names = "-x") int x;
@Option(names = "-y") int y;
}
@Command
class App {
@Command
public void doit(@Mixin CommonParams params, @Option(names = "-z") int z) {}
}
In the above example, the -x
and -y
options are added to the -z
-option of the doit
command.
@Spec
Annotation
21.4. Picocli 3.2 introduces a @Spec
annotation for injecting the CommandSpec
model of the command into a command field.
|
From picocli 4.6, @Spec -annotated elements can be used in ArgGroup classes.
|
This is useful when a command needs to use the picocli API, for example to walk the command hierarchy and iterate over its sibling commands.
This complements the @ParentCommand
annotation; the @ParentCommand
annotation injects a user-defined command object, whereas this annotation injects a picocli class.
java@Command
class InjectSpecExample implements Runnable {
@Spec CommandSpec commandSpec;
//...
public void run() {
// do something with the injected spec
System.out.println("My full name is " + commandSpec.qualifiedName());
}
}
@Spec(MIXEE)
Annotation
21.4.1. As of picocli 4.3, the @Spec
annotation has a value
element.
The value is Spec.Target.SELF
by default, meaning that the CommandSpec
of the enclosing class is injected into the @Spec
-annotated field.
For classes that are used as a mixin, there is another value that may be useful.
When @Spec(Spec.Target.MIXEE)
is specified in a mixin class, the CommandSpec
of the command receiving this mixin (the "mixee") is injected into the @Spec
-annotated field.
This can be useful when a mixin contains logic that is common to many commands.
See Accessing the Mixee from a Mixin for more details.
21.5. Custom Factory
Declaratively registered subcommands, type converters and version providers must be instantiated somehow. As of picocli 2.2, a custom factory can be specified when constructing a CommandLine
instance. This allows full control over object creation and opens possibilities for Inversion of Control and Dependency Injection (see next section). For example:
javaIFactory myFactory = getCustomFactory();
CommandLine cmdLine = new CommandLine(new Git(), myFactory);
Custom factories need to implement the picocli.CommandLine.IFactory
interface:
javapublic interface IFactory {
/**
* Creates and returns an instance of the specified class.
* @param clazz the class to instantiate
* @param <K> the type to instantiate
* @return the new instance
* @throws Exception an exception detailing what went wrong when creating the instance
*/
<K> K create(Class<K> clazz) throws Exception;
}
If no factory is specified, a default factory is used. The default factory requires that the classes to instantiate have a public no-argument constructor: it instantiates the class by first calling clazz.newInstance()
, and if that fails, clazz.getDeclaredConstructor().newInstance()
.
From version 4.0, picocli delegates all object creation to the factory, including creating Collection
instances to capture multi-value @Option
values.
Previously, Collection
objects were instantiated separately without involving the factory.
It is recommended that custom factories should fall back to the default factory. Something like this:
java@Override
public <K> K create(Class<K> clazz) throws Exception {
try {
return doCreate(clazz); // custom factory lookup or instantiation
} catch (Exception e) {
return CommandLine.defaultFactory().create(clazz); // fallback if missing
}
}
21.6. Model Transformations
From picocli 4.6, it is possible to use the annotations API to modify the model (commands, options, subcommands, etc.) dynamically at runtime.
The @Command
annotation now has a modelTransformer
attribute where applications can specify a class that implements the IModelTransformer
interface:
javainterface IModelTransformer {
/**
* Given an original CommandSpec, return the object that should be used
* instead. Implementors may modify the specified CommandSpec and return it,
* or create a full or partial copy of the specified CommandSpec, and return
* that, or even return a completely new CommandSpec.
* <p>
* Implementors are free to add or remove options, positional parameters,
* subcommands or modify the command in any other way.
* </p><p>
* This method is called once, after the full command hierarchy is
* constructed, and before any command line arguments are parsed.
* </p>
* @return the CommandSpec to use instead of the specified one
*/
CommandSpec transform(CommandSpec commandSpec);
}
This allows applications to dynamically add or remove options, positional parameters or subcommands, or modify the command in any other way, based on some runtime condition.
java@Command(modelTransformer = Dynamic.SubCmdFilter.class)
class Dynamic {
static class SubCmdFilter implements IModelTransformer {
public CommandSpec transform(CommandSpec commandSpec) {
if (Boolean.getBoolean("disable_sub")) {
commandSpec.removeSubcommand("sub");
}
return commandSpec;
}
}
@Command
private void sub() {
// subcommand, business logic
}
}
All transformers are called once, after the full command hierarchy is constructed, and before any command line arguments are parsed.
|
If your model transformer is declared as nested class, make sure you mark this class as static , or picocli will not be able to instantiate your transformer class.
|
21.7. Automatic Parameter Indexes
21.7.1. Automatic Indexes
Positional parameters have an index
attribute that determines which command line argument(s) are captured.
It is possible to omit the index
attribute and let picocli automatically assign an index.
This means different things for single-value and for multi-value positional parameters.
For multi-value positional parameters (arrays or collections), omitting the index
attribute means the field captures all positional parameters (the equivalent of index = "0..*"
).
For single-value positional parameters, picocli’s behaviour has changed since version 4.3:
prior to picocli 4.3, the default index for single-value positional parameters was also index = "0..*"
, even though only one value (usually the first argument) can be captured.
From version 4.3, the default index is index = "0+"
, which tells picocli to assign an index automatically, starting from zero, based on the other positional parameters defined in the same command.
A simple example can look like this:
javaclass AutomaticIndex {
@Parameters(hidden = true) // "hidden": don't show this parameter in usage help message
List<String> allParameters; // no "index" attribute: captures _all_ arguments
@Parameters String group; // assigned index = "0"
@Parameters String artifact; // assigned index = "1"
@Parameters String version; // assigned index = "2"
}
Picocli initializes fields with the values at the specified index in the arguments array.
javaString[] args = { "info.picocli", "picocli", "4.3.0" };
AutomaticIndex auto = CommandLine.populateCommand(new AutomaticIndex(), args);
assert auto.group.equals("info.picocli");
assert auto.artifact.equals("picocli");
assert auto.version.equals("4.3.0");
assert auto.allParameters.equals(Arrays.asList(args));
|
Automatic indexes depend on the ability of Java reflection and Java annotation processors to iterate over fields in declaration order in the source code.
Officially this is not guaranteed by the Java spec.
In practice this has worked in Oracle JVMs and OpenJDK from Java 6, but there is some risk this may not work in the future or on other JVMs.
In general, for single-value positional parameters, using explicit indexes is the safer option.
(Multi-value positional parameters can safely omit the |
|
Methods cannot be iterated over in predictable order.
For applications with @Parameters -annotated methods or combinations of @Parameters -annotated methods and @Parameters -annotated fields, we recommend using explicit indexes for single-value positional parameters.
|
21.7.2. Anchored Automatic Index
The default automatic index (index = "0+"
) for single-value positional parameters is "anchored at zero": it starts at zero, and is increased with each additional positional parameter.
Sometimes you want to have indexes assigned automatically from a different starting point than zero. This can be useful when defining Mixins with positional parameters.
To accomplish this, specify an index with the anchor point and a +
character to indicate that picocli should start to automatically assign indexes from that anchor point. For example:
javaclass Anchored {
@Parameters(index = "1+") String p1; // assigned index = "1" or higher
@Parameters(index = "1+") String p2; // assigned index = "2" or higher
}
21.7.3. Combining Explicit and Automatic Indexes
If a command defines a positional parameter with an explicit index at the anchor point, then automatic indexes anchored at that point will start from the explicit index plus 1. For example:
javaclass ExplicitAndAutomaticIndexes {
@Parameters(index = "0" ) String explicit0; // explicit index "0" at default anchor point
@Parameters String pAuto1; // assigned index "1", anchor point + 1
@Parameters String pAuto2; // assigned index "2"
@Parameters(index = "1+") String pAnchored1; // assigned index "1": no explicit index 1
@Parameters(index = "1+") String pAnchored2; // assigned index "2"
@Parameters(index = "2" ) String explicit2; // explicit index "2", matching anchor for "2+"
@Parameters(index = "2+") String pAnchored3; // assigned index "3", anchor point + 1
@Parameters(index = "2+") String pAnchored4; // assigned index "4"
}
21.7.4. Unanchored Automatic Index
Sometimes you want to have indexes assigned automatically to come at the end. This can be useful when defining Mixins with positional parameters.
To accomplish this, specify an index with a +
character to indicate that picocli should automatically assign indexes that come at the end.
For example:
javaclass Unanchored {
@Parameters(index = "0" ) String p0;
@Parameters(index = "1+") String p1; // assigned index = "1"
@Parameters(index = "1+") String p2; // assigned index = "2"
@Parameters(index = "3" ) String p3;
@Parameters(index = "+" ) String p4; // assigned index = "4" <-- unanchored
@Parameters(index = "+" ) String p5; // assigned index = "5" <-- unanchored
}
21.7.5. Do Not Combine Unanchored Indexes with Open-ended Indexes
It is not possible to combine unanchored indexes (+
) with open-ended explicit indexes (*
):
the parameter with the open-ended index will capture all arguments, and there is no position "after the other indexes" that can be assigned.
The below example will always give a Missing required parameter: <last>
error:
javaclass BadCombination {
@Parameters(index = "0..*") List<String> all;
@Parameters(index = "+" ) String last;
}
21.8. Improved Support for Chinese, Japanese and Korean
Picocli will align the usage help message to fit within some user-defined width (80 columns by default). A number of characters in Chinese, Japanese and Korean (CJK) are wider than others. If those characters are treated to have the same width as other characters, the usage help message may extend past the right margin.
From 4.0, picocli will use 2 columns for these wide characters when calculating where to put line breaks, resulting in better usage help message text.
This can be switched off with CommandLine.setAdjustLineBreaksForWideCJKCharacters(false)
.
21.9. Boolean Options with Parameters
By default the value of a boolean field is set to the logical negative of its default value when the option is specified on the command line.
It is possible to let end users explicitly specify "true" or "false" as a parameter for a boolean option by defining an explicit Arity attribute. A boolean option with arity = "0..1"
accepts zero to one parameters, arity = "1"
means the option must have one parameter. For example:
javaclass BooleanOptionWithParameters {
@Option(names = "-x", arity = "1", description = "1 mandatory parameter")
boolean x;
@Option(names = "-y", arity = "0..1", description = "min 0 and max 1 parameter")
boolean y;
}
The following ways to invoke the program will be accepted (values are not case sensitive):
<command> -x true <command> -x FALSE <command> -x TRUE -y <command> -x True -y False
But trying to specify the -x
option without a parameter, or with a value other than "true" or "false" (case insensitive) will result in a ParameterException
.
21.10. Hexadecimal Values
Numeric values are interpreted as decimal numbers by default. If you want picocli to be more flexible, you can register a custom type converter that delegates to the decodehttps://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#decode-java.lang.String- method to convert strings to numbers.
|
The decode method looks at the prefix to determine the radix, so numbers
starting with 0x , 0X or # are interpreted as hexadecimal numbers, numbers starting with 0 are interpreted
as octal numbers, and otherwise the number is interpreted as a decimal number.
|
javanew CommandLine(obj)
.registerConverter(Byte.class, Byte::decode)
.registerConverter(Byte.TYPE, Byte::decode)
.registerConverter(Short.class, Short::decode)
.registerConverter(Short.TYPE, Short::decode)
.registerConverter(Integer.class, Integer::decode)
.registerConverter(Integer.TYPE, Integer::decode)
.registerConverter(Long.class, Long::decode)
.registerConverter(Long.TYPE, Long::decode);
javaITypeConverter<Integer> intConverter = new ITypeConverter<Integer>() {
public Integer convert(String s) {
return Integer.decode(s);
}
};
commandLine.registerConverter(Integer.class, intConverter);
commandLine.registerConverter(Integer.TYPE, intConverter);
// ...
21.11. Option-Parameter Separators
21.11.1. Default Separators
Options may take an option parameter (also called option-argument).
For POSIX-style short options (like -f
or -c
), the option parameter may be attached to the option,
or it may be separated by a space or the separator string (=
by default).
That is, all of the below are equivalent:
bash<command> -foutput.txt
<command> -f output.txt
<command> -f=output.txt
Long option names (like --file
) must be separated from their option parameter by a space or the
separator string (=
by default). That is, the first two below examples are valid but the last example is invalid:
bash// valid (separator between --file and its parameter)
<command> --file output.txt
<command> --file=output.txt
// invalid (picocli will not recognize the --file option when attached to its parameter)
<command> --fileoutput.txt
21.11.2. Custom Separators
The separator string can be customized programmatically or declaratively.
Use the separator
attribute of the @Command
annotation to declaratively set a separator string:
java@Command(separator = ":") // declaratively set a separator
class OptionArg {
@Option(names = { "-f", "--file" }) String file;
}
javaOptionArg optionArg = CommandLine.populateCommand(new OptionArg(), "-f:output.txt");
assert optionArg.file.equals("output.txt");
Alternatively, the separator string can be changed programmatically with the CommandLine.setSeparator(String separator)
method.
For example:
javaOptionArg optionArg = new OptionArg();
CommandLine commandLine = new CommandLine(optionArg);
commandLine.setSeparator(":"); // programmatically set a separator
commandLine.parseArgs("-f:output.txt");
assert optionArg.file.equals("output.txt");
21.12. Best Practices for Command Line Interfaces
Picocli makes it easy to follow this new and modern set of Command Line Interface Guidelineshttps://clig.dev/#guidelines.
Slightly older, but a classic: when designing your command line application, the GNU recommendationshttps://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html#Command_002dLine-Interfaces for command line interfaces and POSIX Utility Guidelineshttps://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02 may be useful.
Generally, many applications use options for optional values and parameters for mandatory values. However, picocli lets you make options required if you want to, see Required Arguments.
21.13. Text Blocks for Java 15
When writing your command line program, you can use Java 15’s new "Text Block" feature. Multi-line text blocks can be used in command and option descriptions, headers and footers.
java@Option(names = { "-v", "--verbose" },
description = """
Verbose mode. Helpful for troubleshooting.
Multiple -v options increase the verbosity.
""") // write description in text block
For more details, see this articlehttps://www.infoq.com/articles/java-text-blocks/ by Java Language Architect Brian Goetz.
22. Dependency Injection
22.1. Guice Example
Subcommands as methods Supported, subcommands can be defined as Unsupported Supported, both the built-in one and custom ones. Unsupported Exit codes Supported: scripts can override Unsupported Command execution Via invocation of Execution after parsing is defined in Custom handling of invalid user input Scripts can override Invalid input handling can be customized by overriding Custom error handling Scripts can override Runtime exception handling can be customized by overriding Principle Implements Script body is transformed to the
PicocliBaseScript2
PicocliBaseScript
@Command
-annotated methods in the script.
help
subcommands
afterExecution(CommandLine, int, Exception)
to call System.exit
.
CommandLine::execute
https://picocli.info/#execute. Scripts can override beforeParseArgs(CommandLine)
to install a custom IExecutionStrategy
.PicocliBaseScript::run
and is not easy to customize. Any subcommand and the main script are both executed.
beforeParseArgs(CommandLine)
to install a custom IParameterExceptionHandler
.PicocliBaseScript::handleParameterException
.
beforeParseArgs(CommandLine)
to install a custom IExecutionExceptionHandler
.PicocliBaseScript::handleExecutionException
.
Callable<Object>
, script body is transformed to the call
method.runScriptBody
method.
When upgrading scripts from picocli versions older than 4.0, just changing the version number is not enough!
Scripts should use
@Grab('info.picocli:picocli-groovy:4.7.1')
. The old artifact id @Grab('info.picocli:picocli:4.7.1')
will not work,
because the @picocli.groovy.PicocliScript
annotation class and supporting classes have been moved into a separate module, picocli-groovy
.
30.1.3. Closures in Annotations
As of picocli 4.6, Groovy programs can use closures in the picocli annotations instead of specifying a class. This can be especially useful in Groovy scripts, where one cannot define a static inner class.
Example:
groovy@Command(name = "ClosureDemo",
versionProvider = {
{ -> ["line1" , "line2"] as String[] } as IVersionProvider 1
},
defaultValueProvider = {
{ argSpec -> "some default" } as IDefaultValueProvider 2
})
class ClosureDemo {
@Option(names = '-x', completionCandidates = {["A", "B", "C"]}) 3
String x
@Option(names = '-y',
parameterConsumer = {
{ args, argSpec, commandSpec -> 4
argSpec.setValue(args.toString() + commandSpec.name())
args.clear()
} as IParameterConsumer
})
String y
@Option(names = '-z', converter = [ // requires Groovy 3.0.7
{ { str -> MessageDigest.getInstance(str) } as ITypeConverter } 5
])
MessageDigest z
}
When a class is specified, picocli creates an instance of the class. By contrast, when a closure is specified, picocli calls the closure to get an instance. (To be precise, both of these are delegated to the configured factory, and the default factory implementation supports closures from picocli 4.6.)
As you can see in the above example, each closure in the annotation should contain another closure that has the required type (IVersionProvider
, IDefaultValueProvider
, etc.)
1 | Command versionProvider : note the empty parameter list before the → arrow. This is needed to help the Groovy compiler. The closure must be cast to IVersionProvider . |
2 | Command defaultProvider : return a default value for the specified ArgSpec parameter. The closure must be cast to IDefaultValueProvider . |
3 | Option or Parameters completionCandidates : return a list of Strings. No parameter list or casting is required. |
4 | Option or Parameters parameterConsumer : given a Stack , ArgSpec and CommandSpec , process the remaining arguments. The closure must be cast to IParameterConsumer . |
5 | Option or Parameters type converter takes an array of closures. Groovy 3.0.7 or greater is required: older versions of Groovy ignore closures in class array annotations. Each closure must have a parameter and be cast to ITypeConverter . |
|
From picocli version 4.7.0, this feature can be disabled by setting system property picocli.disable.closures to true . Disabling support for closures in annotations can improve the application startup time.
|
30.1.4. Older Versions of Groovy
|
When using a Groovy version older than 2.4.7, use this workaround for the Grape bughttps://issues.apache.org/jira/browse/GROOVY-7613 that causes this error:
java.lang.ClassNotFoundException: # Licensed to the Apache Software Foundation (ASF) under one or more .
|
groovy@Grab('info.picocli:picocli-groovy:4.7.1')
@GrabExclude('org.codehaus.groovy:groovy-all') // work around GROOVY-7613
...
In order to get you started quickly, you may have a look at the Groovy exampleshttps://github.com/remkop/picocli/tree/main/picocli-examples/src/main/groovy/picocli/examples folder of the picocli code repository.
30.2. Kotlin
30.2.1. Kotlin Annotation Syntax
Kotlin 1.2 (released Nov 28, 2017) officially supports array literals in annotationshttps://kotlinlang.org/docs/reference/whatsnew12.html#array-literals-in-annotations, allowing a more compact notation:
kotlin@Command(name = "MyApp", version = ["Kotlin picocli v4.0 demo"],
mixinStandardHelpOptions = true, // add --help and --version options
description = ["@|bold Kotlin|@ @|underline picocli|@ example"])
class MyApp : Callable<Int> {
@Option(names = ["-c", "--count"], paramLabel = "COUNT",
description = ["the count"])
private var count: Int = 1
override fun call(): Int {
for (i in 0 until count) {
println("hello world $i...")
}
return 0
}
}
fun main(args: Array<String>) = System.exit(CommandLine(MyApp()).execute(*args))
When specify a class as an argument of an annotation, use a Kotlin class (KClasshttps://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/index.html). The Kotlin compiler will automatically convert it to a Java class, so that the Java code will be able to see the annotations and arguments normally.
kotlin@Command(name = "top", // ...
subcommands = [SubCmd::class, picocli.CommandLine.HelpCommand::class])
class TopCmd { // ...
}
@Command(name = "sub", /* ... */)
class SubCmd { // ...
}
30.2.2. Older Versions of Kotlin
Kotlin versions prior to 1.2 did not allow the array literal syntax in annotationshttps://kotlinlang.org/docs/reference/annotations.html, so with older versions of Kotlin you will have to write arrayOf(…)
for the names
, description
and type
attributes.
kotlin@Command(name = "MyApp", version = arrayOf("picocli demo for Kotlin v1.0 and Kotlin v1.1"),
mixinStandardHelpOptions = true, // add --help and --version options
description = arrayOf("@|bold Kotlin|@ @|underline picocli|@ example"))
class MyApp : Callable<Int> {
@Option(names = arrayOf("-c", "--count"),
description = arrayOf("number of repetitions"))
private var count: Int = 1
override fun call(): Int {
for (i in 0 until count) {
println("hello world $i...")
}
return 0
}
}
fun main(args: Array<String>) = System.exit(CommandLine(MyApp()).execute(*args))
In order to get you started quickly, you may have a look at the Kotlin exampleshttps://github.com/remkop/picocli/tree/main/picocli-examples/src/main/kotlin/picocli/examples/kotlin folder of the picocli code repository.
30.3. Scala
Scala does not allow specifying array annotation attribute as a single value,
so be aware that you will have to write Array(…)
for the names
, description
and type
attributes.
scala@Command(name = "MyApp", version = Array("Scala picocli demo v4.0"),
mixinStandardHelpOptions = true, // add --help and --version options
description = Array("@|bold Scala|@ @|underline picocli|@ example"))
class MyApp extends Callable[Int] {
@Option(names = Array("-c", "--count"), paramLabel = "COUNT",
description = Array("the count"))
private var count: Int = 1
def call() : Int = {
for (i <- 0 until count) {
println(s"hello world $i...")
}
0
}
}
object MyApp {
def main(args: Array[String]): Unit = {
System.exit(new CommandLine(new MyApp()).execute(args: _*))
}
}
In order to get you started quickly, you may have a look at the Scala exampleshttps://github.com/remkop/picocli/tree/main/picocli-examples/src/main/scala/picocli/examples/scala folder of the picocli code repository.
31. API JavaDoc
Picocli API JavaDoc can be found here.
32. GitHub Project
The GitHub projecthttps://github.com/remkop/picocli has the source code, tests, build scripts, etc.
Star and/or fork this project on GitHub if you like it!
33. Issue Tracker
The GitHub Issue Trackerhttps://github.com/remkop/picocli/issues can be used to report bugs or request features. There is also a Mailing List, and for questions where the user community may know the answer, StackOverflowhttps://stackoverflow.com/questions/tagged/picocli is both a good resource and a great way to build an online knowledge base.
34. Mailing List
Join the picocli Google grouphttps://groups.google.com/d/forum/picocli if you are interested in discussing anything picocli-related and receiving announcements on new releases.
35. License
Picocli is licensed under the Apache License 2.0https://github.com/remkop/picocli/blob/main/LICENSE.
36. Releases
Previous versions are available from the GitHub project Releaseshttps://github.com/remkop/picocli/releases.
37. Download
You can add picocli as an external dependency to your project, or you can include it as source.
37.1. Build tools
groovyimplementation 'info.picocli:picocli:4.7.1'
37.2. Source
By using picocli in source form, you can avoid having an external dependency on picocli.
Picocli has only one source file: CommandLine.javahttps://github.com/remkop/picocli/blob/v4.7.1/src/main/java/picocli/CommandLine.java.
This facilitates including picocli in your project: simply copy and paste the code of this file into a file called CommandLine.java
, add it to your project, and enjoy!
Last updated 2023-01-27 18:22:36 +0900