Groovy Scripting — Writing Scripts and Automating Workflows
In this tutorial, you will learn about Groovy Scripting. We cover key concepts, practical examples, and best practices to help you master this topic.
Groovy scripting enables writing executable scripts without class declarations, with bindings, command-line arguments, and seamless Java integration for automation tasks.
What You'll Learn
- Script structure and execution
- Bindings and variable scope
- Command-line arguments
- Script compilation and caching
Why It Matters
Groovy scripts replace shell scripts with full Java library access. Doda Browser uses Groovy scripts for build automation, release management, and deployment workflows. Durga Antivirus Pro uses scripts for signature update automation.
Real-World Use
DevOps automation, data processing pipelines, build scripts, system administration, and rapid prototyping where Java's full ecosystem is needed without compilation overhead.
flowchart LR
A["Groovy Script"] --> B["Variables"]
A --> C["Bindings"]
A --> D["Args"]
B --> E["Script Scope"]
C --> F["Shared State"]
D --> G["CLI Parameters"]
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
Your First Script
#!/usr/bin/env groovy
// hello.groovy
println "Hello, Groovy!"
println "Date: ${new Date()}"
println "User: ${System.getProperty('user.name')}"
Run with: groovy hello.groovy
No class declaration, no main method — just code.
Script Variables and Scope
// Script-level variables
def localVar = "local" // Local — not visible to binding
scriptVar = "global" // Script binding — visible everywhere
def myMethod() {
// println localVar // ERROR: not visible
println scriptVar // OK — visible
}
myMethod()
println scriptVar // OK
Command-Line Arguments
// args.groovy
println "Count: ${args.length}"
args.eachWithIndex { arg, i ->
println "arg[$i] = $arg"
}
groovy args.groovy hello world 42
# Count: 3
# arg[0] = hello
# arg[1] = world
# arg[2] = 42
Script Bindings
Bindings allow scripts to share state:
// script1.groovy
binding.setProperty('sharedData', [1, 2, 3])
binding.setProperty('userName', 'Alice')
println "script1 done"
// Run with: groovy script1.groovy && groovy script2.groovy
// script2.groovy
println "Shared data: ${binding.getProperty('sharedData')}"
println "User: ${binding.getProperty('userName')}"
Bindings persist only within the same JVM invocation.
Reading Files in Scripts
def configFile = new File('config.properties')
if (configFile.exists()) {
def props = new Properties()
configFile.withInputStream { props.load(it) }
props.each { key, value ->
println "$key = $value"
}
} else {
println "Config file not found"
System.exit(1)
}
External Process Execution
// Run shell commands
def result = "ls -la".execute()
println result.text
def gitLog = "git log --oneline -5".execute()
println "Recent commits:"
gitLog.text.eachLine { println " $it" }
// With error handling
def process = "gradle build".execute()
process.waitFor()
if (process.exitValue() != 0) {
System.err.println "Build failed:"
System.err.println process.err.text
}
Script as a Library
Scripts can define methods and be reused:
// utils.groovy
def greet(String name) { "Hello, $name!" }
def doubleIt(int n) { n * 2 }
def sum(List nums) { nums.sum() }
// Return the methods so they can be used
this.metaClass.methods.findAll { !it.name.contains('$') }
Other scripts can evaluate this script to access its methods.
Compiling Scripts
// Pre-compile for performance
// groovyc -d classes script.groovy
// java -cp "groovy-all.jar:classes" script
// Or use groovy compilescript
Pre-compilation removes Parsing time but still requires the Groovy runtime.
Script with CliBuilder
import groovy.cli.CliBuilder
def cli = new CliBuilder(usage: 'backup.groovy -s source -d dest')
cli.with {
s 'Source directory', args: 1, required: true
d 'Destination directory', args: 1, required: true
v 'Verbose mode'
f 'Force overwrite'
}
def options = cli.parse(args)
if (!options) return
if (options.v) println "Verbose mode enabled"
println "Backup from ${options.s} to ${options.d}"
Common Mistakes
1. Script scope confusion
Variables declared with def are local to the script class, not the binding. Use bare assignments for binding variables.
2. Not handling script errors
Use try/catch around file operations and external commands. Scripts often run unattended.
3. Platform-specific paths
Use File.separator or / paths. Windows uses \. Groovy handles both with /.
4. Large data in memory
Scripts processing large files should use streaming (eachLine, withInputStream), not reading the whole file at once.
5. Missing shebang for direct execution
#!/usr/bin/env groovy at the top makes scripts directly executable on Unix.
Practice Questions
1. How do you run a Groovy script?
groovy filename.groovy. On Unix, make it executable with shebang and chmod +x.
2. What is a script binding?
A key-value store shared across scripts or between script methods. Access via binding.getProperty and binding.setProperty.
3. How do you access command-line arguments?
The args variable is automatically available as a String[] array.
4. What happens if a script has no main method?
Scripts don't need a main method. All top-level code is executed sequentially when the script runs.
Challenge: Write a Groovy script that monitors a directory for new files and logs them to a CSV.
FAQ
{{< faq question="Can Groovy scripts import Java libraries?" >}}
Yes. Scripts have full access to the Java classpath. Use import statements like in regular Groovy.
{{< /faq >}}
{{< faq question="How do I pass arguments to a Groovy script?" >}}
As space-separated values after the script name. They appear in the args array.
{{< /faq >}}
{{< faq question="Can a Groovy script be compiled to a JAR?" >}}
Yes. Use groovyc to compile the script and jar the resulting classes.
{{< /faq >}}
{{< faq question="What is the GroovyShell class?" >}}
GroovyShell evaluates scripts programmatically from Java or Groovy code, with custom bindings and classloaders.
{{< /faq >}}
{{< faq question="Can scripts be encrypted or obfuscated?" >}} Yes, like any Java code. Compile to bytecode and apply standard Java obfuscation tools. {{< /faq >}}
Mini Project
Create a log analyzer script that reads server logs, counts errors per hour, and generates a summary report.
def logFile = new File(args[0] ?: 'server.log')
def hourlyErrors = [:]
logFile.eachLine { line ->
if (line.contains('ERROR') || line.contains('FATAL')) {
def matcher = line =~ /(\d{4}-\d{2}-\d{2} \d{2}):\d{2}:\d{2}/
if (matcher) {
def hour = matcher[0][1]
hourlyErrors[hour] = (hourlyErrors[hour] ?: 0) + 1
}
}
}
println "Hourly Error Report"
println "===================="
hourlyErrors.sort().each { hour, count ->
println "$hour: $count errors"
}
def reportFile = new File('error-report.txt')
reportFile.text = hourlyErrors.collect { k, v -> "$k,$v" }.join('\n')
println "Report written to ${reportFile.absolutePath}"
What's Next
Now that you understand scripting, proceed to AST transformations.
| Topic | Description | Link |
|---|---|---|
| AST transformations | Compile-time Metaprogramming | {{< ref "26-ast-transformations" >}} |
| Grape | Dependency management | {{< ref "27-grape" >}} |
| Spock | Testing framework | {{< ref "16-spock" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro