How to Use Groovy's CliBuilder
Let’s use Groovy’s CliBuilder
to create CLI programs that can take flags.
Intro
I write quite some scripts at work, either for myself or for my team. I
used to write these scripts in Bash, but since we are using Windows at
work, it made sense to switch to a more general scripting language.
Because we’re a Java shop, Groovy seemed like a natural choice. Now,
it’s often very convenient to have named command-line arguments (e.g.
tool --output out.ext --input in.ext
) as opposed to positional
arguments (e.g. tool out.ext in.ext
): named arguments allow for
putting the arguments in any order you want, and you can leave out
optional arguments and use the defaults. Groovy’s CliBuilder
makes
things especially easy.
A sample program
weather.groovy
@Grab(group='commons-cli', module='commons-cli', version='1.4')
class Main {
static main(args) {
CommandLineInterface cli = CommandLineInterface.INSTANCE
cli.parse(args)
}
}
enum CommandLineInterface {
INSTANCE
CliBuilder cliBuilder
CommandLineInterface() {
cliBuilder = new CliBuilder(
usage: 'weather [<options>]',
header: 'Options:',
footer: 'And here we put footer text.'
)
// set the amount of columns the usage message will be wide
cliBuilder.width = 80 // default is 74
cliBuilder.with {
h longOpt: 'help', 'Print this help text and exit.'
n(longOpt: 'max-count', args: 1, argName: 'number',
'Limit the number of days shown')
'1' longOpt: 'one', "Show today's forecast"
'2' longOpt: 'two', "Show today's and tomorrow's forecast"
_(longOpt: 'url', args: 1, argName: 'URL',
'Use given URL to query for weather.')
D(args: 2, valueSeparator: '=', argName: 'property=value',
'Use value for given property.')
}
}
void parse(args) {
OptionAccessor options = cliBuilder.parse(args)
if (!options) {
System.err << 'Error while parsing command-line options.\n'
System.exit 1
}
if (options.h) {
cliBuilder.usage()
System.exit 0
}
if (options.n) {
// handle user's input
}
if (options.'1') {
// show weather for one day
}
if (options.url) {
URI uri = new URI(options.url)
}
if (options.Ds) {
assert options.Ds.class == ArrayList.class
}
}
}
When invoked with groovy weather.groovy --help
, the program will
output:
usage: weather []
Options:
-1,--one Show today's forecast
-2,--two Show today's and tomorrow's forecast
-D Use value for given property.
-h,--help Print this help text and exit.
-n,--max-count Limit the number of days shown
--url Use given URL to query for weather.
And here can put footer text.
You can specify short and long names: cliBuilder.n
specifies that the
program will accept an argument -n
. To also specify a long name, we
add longOpt: max-count
. To use only a long name, we can replace the
short name by _
: cliBuilder._(longName: 'wow-such-long')
. Note that
cliBuilder._
can be reused several times (in contrast to other short
names).
The number of arguments can be indicated by using the args
parameter.
If you use more than one parameter, a valueSeparator
must be
indicated, which is used to split up the argument. I found that the
number assigned to args
is not very strict: when called with argument
-Dmyprop
, the following is valid.
assert options.Ds == ['myprop']
Note how we append an s
to the option name (options.Ds
) to get a
list of properties and values. A normal invocation groovy weather.groovy -Dpancakes=yesplease
would result in:
assert options.Ds == ['pancakes', 'yesplease']
When required: true
is set, the program will shout at you if the
argument is not used. For example, if we specify cliBuilder.q(required: true)
but fail to provide the -q
argument on the command line, the
program will exit with error: Missing required option: q
.
One thing to remember is that, if you want to use digits as argument
names, you have to stringify them first by putting quotation marks
around them: cliBuilder.'1'
.
More on CliBuilder
Check the Groovy documentation on CliBuilder for more information.
Dependencies
Groovy’s CliBuilder
depends on cli-commons
:
build.gradle
dependencies {
compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
}