Groovy Command-Line Scripts
last modified October 15, 2026
Groovy scripts are often used as command-line tools: small programs for file
processing, automation, and quick data checks. In this tutorial, we run Groovy
scripts with the groovy command, make them executable with a
shebang line, read arguments, parse options with CliBuilder,
return exit codes, and use the Binding that holds script variables.
Running a Script with the groovy Command
The groovy launcher runs a script file. Everything after the file
name is passed to the script as command-line arguments. The -e
option runs a one-line expression without creating a file.
#!/usr/bin/env groovy
println "Script name: ${this.class.simpleName}"
println "Arguments count: ${args.length}"
args.eachWithIndex { arg, i ->
println "arg[$i] = $arg"
}
The script prints its class name, the number of arguments, and each argument
with its index. The first line is a shebang, which we discuss in the next
section; the groovy launcher ignores it.
println "Script name: ${this.class.simpleName}"
Each Groovy script is compiled into a class. this.class.simpleName
returns the name of that class, which is derived from the file name, here
hello.
args.eachWithIndex { arg, i ->
println "arg[$i] = $arg"
}
args is an array of strings with the command-line arguments.
eachWithIndex gives us each element together with its position.
Arguments that contain spaces must be quoted by the shell.
$ groovy hello.groovy one "two three" Script name: hello Arguments count: 2 arg[0] = one arg[1] = two three
Evaluating a One-Liner with -e
The one-liner form is useful for quick checks:
$ groovy -e "println 'Hello from -e'" Hello from -e
Shebang Line
On Linux and macOS, a shebang line at the top of a script lets the operating
system run the file directly. Groovy accepts a line starting with #!
and ignores it when compiling. Use /usr/bin/env groovy so the
script finds groovy on the PATH.
The chmod +x command sets the executable bit. After that, the
script can be started by its path, without typing groovy. On
Windows, run the script with groovy hello.groovy.
$ chmod +x hello.groovy $ ./hello.groovy a b Script name: hello Arguments count: 2 arg[0] = a arg[1] = b
Reading Standard Input
Command-line tools often read data from a pipe. Inside a script, the standard
input stream is available as System.in. The text
property reads the whole stream into a string.
$ echo "piped line" | groovy -e "println System.in.text.trim().toUpperCase()" PIPED LINE
Exit Codes
A script exits with code 0 when it finishes normally. Shell
scripts and CI pipelines check this value, so a failing script should return a
non-zero code. System.exit stops the script with the given code.
def file = new File(args[0])
if (!file.exists()) {
System.err.println "File not found: ${file}"
System.exit(3)
}
println "Lines: ${file.readLines().size()}"
The script writes the error message to standard error, which keeps it separate
from normal output, and exits with code 3 when the file is missing.
If the script is started without any argument, args[0] throws an
exception. Groovy prints the stack trace and the launcher exits with code
1. Checking args.size() first gives a clearer message.
$ groovy ExitCode.groovy /nonexistent File not found: /nonexistent $ echo $? 3 $ groovy ExitCode.groovy data.txt Lines: 2 $ echo $? 0
Parsing Options with CliBuilder
For options such as -n name or --verbose,
CliBuilder from the groovy-cli-commons module builds
the parser and the usage text for us. In Groovy 5 the class must be imported
from groovy.cli.commons.
#!/usr/bin/env groovy
import groovy.cli.commons.CliBuilder
def cli = new CliBuilder(usage: 'greet.groovy [-h] [-n name] [-c count] [-v]')
cli.header = 'Options:'
cli.h(longOpt: 'help', 'Show this help message')
cli.n(longOpt: 'name', args: 1, argName: 'name', 'Name to greet')
cli.c(longOpt: 'count', args: 1, argName: 'count', type: Integer, 'How many times')
cli.v(longOpt: 'verbose', 'Print extra output')
def options = cli.parse(args)
if (!options) {
System.exit(2)
}
if (options.h) {
cli.usage()
System.exit(0)
}
def name = options.n ?: 'world'
def count = options.c ? options.c as int : 1
if (options.v) println "verbose: name=$name count=$count"
count.times { println "Hello, $name!" }
Each call such as cli.n(...) declares one option. The
args: 1 setting means the option takes a value, and
longOpt defines the --name form. parse
returns an object whose properties hold the option values, or null
when parsing fails.
def options = cli.parse(args)
if (options.h) { cli.usage() }
Boolean options such as -h are true when present.
The usage method prints the help text generated from the option
definitions.
def count = options.c ? options.c as int : 1
The option value is a string unless a type is declared. Here we
convert it with as int and fall back to 1 when the
option was not given.
$ groovy greet.groovy -n Alice -c 2 -v verbose: name=Alice count=2 Hello, Alice! Hello, Alice! $ groovy greet.groovy -h usage: greet.groovy [-h] [-n name] [-c count] [-v] Options: -c,--count <count> How many times -h,--help Show this help message -n,--name <name> Name to greet -v,--verbose Print extra output $ groovy greet.groovy --count 3 --name Bob Hello, Bob! Hello, Bob! Hello, Bob!
Unknown Options
CliBuilder does not reject unknown options. An argument such as
--bogus is kept in the list returned by options.arguments().
If the script takes no positional arguments, check that list yourself.
import groovy.cli.commons.CliBuilder
def cli = new CliBuilder(usage: 'probe')
cli.v(longOpt: 'verbose', 'Print extra output')
def options = cli.parse(args)
println "arguments: ${options.arguments()}"
$ groovy probe.groovy --bogus file.txt arguments: [--bogus, file.txt]
The Binding
A script runs with a Binding object. The binding is a map of
variables that the script can read and write, and it is available as
binding inside the script. Variables assigned without a
def keyword, and the args array, are stored there.
Local variables declared with def are not.
greeting = "hi from binding"
println binding.getVariable('greeting')
println binding.variables.containsKey('greeting')
def local = 'not in binding'
println binding.variables.containsKey('local')
println "args bound: ${binding.hasVariable('args')}"
greeting = "hi from binding"
The assignment has no def, so Groovy stores greeting
in the binding instead of in a local variable. Other scripts that share the same
binding can read it.
$ groovy Implicit.groovy a b hi from binding true false args bound: true
Passing a Binding to GroovyShell
A Binding can also be created by the host program and passed to a
GroovyShell, which lets you supply values to a script from outside.
def shell = new GroovyShell(new Binding(appName: 'demo', debug: true))
def result = shell.evaluate('"App $appName, debug=$debug"')
println result
def b = new Binding()
b.setVariable('x', 10)
b.y = 5
println "variables: ${b.variables.keySet().sort()}"
def script = new GroovyShell(b).parse('x * y')
println "x * y = ${script.run()}"
The first GroovyShell evaluates a string that refers to
appName and debug; those names are resolved from the
binding. The second example sets x and y through the
binding and runs a parsed script that multiplies them.
$ groovy BindingDemo.groovy App demo, debug=true variables: [x, y] x * y = 50
Best Practices
Use a shebang and chmod +x for tools that you run often. Validate
arguments at the start of the script and exit with a non-zero code on errors.
Write error messages to standard error. Use CliBuilder when a
script has more than one or two options, so the usage text stays in sync with
the code. Prefer def for local variables and reserve the implicit
binding for values that really need to be shared.
Source
This tutorial showed how to run Groovy scripts from the command line, make them
executable with a shebang, read arguments and standard input, parse options with
CliBuilder, return exit codes, and work with the script
Binding.
Author
List all Groovy tutorials.