1. Invoking your Commands
This section addresses how you can control the way in which your commands are invoked.
By Name Versus Positional Parameters
As seen earlier, decorating a method with @ShellMethod is the sole requirement for creating a command.
The user can set the value of all the method parameters in either of two ways:
-
By using a parameter key (for example,
--arg value). This approach is called “by name parameters.” -
Without a key, by setting parameter values in the order in which they appear in the method signature (called “positional parameters”).
These two approaches can be mixed and matched, with named parameters always taking precedence (as they are less prone to ambiguity). Consider the following command definition:
@ShellMethod("Display stuff.")
public String echo(int a, int b, int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
Given the preceding command definiton, the following invocations are all equivalent, as shown in the output:
shell:>echo 1 2 3 (1)
You said a=1, b=2, c=3
shell:>echo --a 1 --b 2 --c 3 (2)
You said a=1, b=2, c=3
shell:>echo --b 2 --c 3 --a 1 (3)
You said a=1, b=2, c=3
shell:>echo --a 1 2 3 (4)
You said a=1, b=2, c=3
shell:>echo 1 --c 3 2 (5)
You said a=1, b=2, c=3
| 1 | This uses positional parameters. |
| 2 | This is an example of full by-name parameters. |
| 3 | By-name parameters can be reordered as desired. |
| 4 | You can use a mix of the two approaches. |
| 5 | The non by-name parameters are resolved in the order in which they appear. |
Customizing the Named Parameter Keys
As seen earlier, the default strategy for deriving the key for a named parameter is to use the Java
name of the method signature and prefix it with two dashes (--). You can customize this in two ways:
-
Use the
prefix()attribute of the@ShellMethodannotation to change the default prefix for the whole method. -
Annotate the parameter with the
@ShellOptionannotation to override the entire key in a per-parameter fashion.
Consider the following example:
@ShellMethod(value = "Display stuff.", prefix="-")
public String echo(int a, int b, @ShellOption("--third") int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
For such a setup, the possible parameter keys are -a, -b and --third.
|
You can specify several keys for a single parameter. If you do so, these keys are mutually exclusive (only one of them can be used) ways
to specify the same parameter. The following example shows the signature of the
built-in
|
Optional Parameters and Default Values
Spring Shell provides the ability to give parameters default values, which lets users omit those parameters. Consider the following command definition:
@ShellMethod("Say hello.")
public String greet(@ShellOption(defaultValue="World") String who) {
return "Hello " + who;
}
With the preceding definition, the greet command can still be invoked as greet Mother (or greet --who Mother), but the following
is also possible:
shell:>greet
Hello World
Parameter Arity
Up to now, it has always been assumed that each parameter maps to a single word entered by the user.
Situations may arise, though, when a parameter value should be multi-valued. This is driven by the arity()
attribute of the @ShellOption annotation. You can use a collection or array for the parameter type and specify how
many values are expected:
@ShellMethod("Add Numbers.")
public float add(@ShellOption(arity=3) float[] numbers) {
return numbers[0] + numbers[1] + numbers[2];
}
The users can then invoke the command by using any of the following syntax:
shell:>add 1 2 3.3
6.3
shell:>add --numbers 1 2 3.3
6.3
|
When using the by-name parameter approach, the key should not be repeated. The following does not work:
|
Varying Amount Arity
The above example demonstrates requiring a known, constant arity for a parameter, three in this case. Allowing any number of multiple values of a parameter can be achieved by leaving arity unspecified and using Spring’s built-in comma separated value parsing for collections and/or arrays:
@ShellMethod("Add a Varying Amount of Numbers.")
public double add(@ShellOption double[] numbers) {
return Arrays.stream(numbers).sum();
}
The command may then be invoked with any amount of numbers:
shell:>add 1,2,3.3
6.3
shell:>add --numbers 42
42.0
shell:>add --numbers 1,2,3.3,4,5
15.3
Special Handling of Boolean Parameters
When it comes to parameter arity, one kind of parameter receives a special treatment by default, as
is often the case in command-line utilities.
Boolean (that is, boolean as well as java.lang.Boolean) parameters behave like they have an arity() of 0 by default, allowing users to set their values by using a “flag” approach.
Consider the following command definition:
@ShellMethod("Terminate the system.")
public String shutdown(boolean force) {
return "You said " + force;
}
This preceding command definition allows the following invocations:
shell:>shutdown
You said false
shell:>shutdown --force
You said true
This special treatment plays well with the default value specification. Although the default
for boolean parameters is to have their default value be false, you can specify otherwise (that is,
@ShellOption(defaultValue="true")), and the behavior is inverted (that is, not specifying the parameter
results in the value being true, and specifying the flag results in the value being false)
|
|
Having this behavior of implicit
|
Quotes Handling
Spring Shell takes user input and tokenizes it into words, splitting on space characters.
If the user wants to provide a parameter value that contains spaces, that value needs to be quoted.
Both single (') and double (") quotes are supported, and those quotes are not part of the value:
Consider the following command definition:
@ShellMethod("Prints what has been entered.")
public String echo(String what) {
return "You said " + what;
}
The following commands all invoke the preceding command definition:
shell:>echo Hello
You said Hello
shell:>echo 'Hello'
You said Hello
shell:>echo 'Hello World'
You said Hello World
shell:>echo "Hello World"
You said Hello World
Supporting both single and double quotes lets the user embed one type of quotes into a value:
shell:>echo "I'm here!"
You said I'm here!
shell:>echo 'He said "Hi!"'
You said He said "Hi!"
That way, the user can use a single quote as an apostrophe in a message.
Should the user need to embed the same kind of quote that was used to quote the whole parameter,
the escape sequence uses the backslash (\) character:
shell:>echo 'I\'m here!'
You said I'm here!
shell:>echo "He said \"Hi!\""
You said He said "Hi!"
shell:>echo I\'m here!
You said I'm here!
It is also possible to escape space characters when not using enclosing quotes:
shell:>echo This\ is\ a\ single\ value
You said This is a single value
Interacting with the Shell
The Spring Shell project builds on top of the JLine library and, as a result, brings a lot of nice interactive features, some of which are detailed in this section.
First and foremost, Spring Shell supports tab completion almost everywhere possible. So, if there
is an echo command and the user types ec and presses TAB, echo appears.
Should there be several commands that start with ec, then the user is prompted to choose (using TAB or
Shift + TAB to navigate and ENTER to select.)
But completion does not stop at command keys. It also works for parameter keys (--arg) and even
parameter values, if the application developer registered the appropriate beans (see [providing-tab-completion]).
Another nice feature of Spring Shell applications is support for line continuation. If a command and its parameters
is too long and does not fit nicely on the screen, a user can chunk it by ending a line with a backslash
(\) character, pressing ENTER, and continuing on the next line. Upon submission of the whole command, this is
parsed as if the user entered a single space on line breaks. The following listing shows an example of this behavior:
shell:>register module --type source --name foo \ (1)
> --uri file:///tmp/bar
Successfully registered module 'source:foo'
| 1 | command continues on next line |
Line continuation also automatically triggers if the user has opened a quote (see Quotes Handling)
and presses ENTER while still in the quotes:
shell:>echo "Hello (1)
dquote> World"
You said Hello World
| 1 | The user pressed ENTER here. |
Finally, Spring Shell applications benefit from a lot of keyboard shortcuts (borrowed from Emacs) with which you may
already be familiar from working with your regular OS Shell. Notable shortcuts include Ctrl+r to perform
a reverse search, Ctrl+a] and Ctrl+e to move to the beginning and the end of the current line (respectively),
and Esc f and Esc b to move forward or backward (respectively) one word at a time.