ZetCode

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.

hello.groovy
#!/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.

ExitCode.groovy
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.

greet.groovy
#!/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.

Implicit.groovy
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.

BindingDemo.groovy
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

Groovy documentation

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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than eight years of experience in teaching programming.

List all Groovy tutorials.