GROOVYON
THESHELL
SHELLSCRIPTINGWITHGROOVY
Alexander Klein /http://codecentric.de alexander.klein@codecentric.de
0
WHY?
GROOVY
IS
COOL
BECAUSE
I
CAN
TODOCOMPLEXSTUFF
TESTINGYOURSCRIPTS
IFYOUKNOWGROOVY
BETTERTHANBASH
ALEXANDER(SASCHA)KLEIN
 
Principal Consultant
codecentric AG
Stuttgart
 
Groovy, JavaFX, UI / UX
 
Griffon committer
 
alexander.klein@codecentric.de
@saschaklein
http://gplus.to/karfunkel
EXECUTING
GROOVY
SCRIPTS$ groovy Test.groovy
SHE-BANG-COMMENT(#!)
#!/usr/local/bin/groovy
USINGTHEDEFAULT
ENVIRONMENT
#!/usr/bin/env groovy
DEFININGTHECLASSPATH
#!/usr/bin/env groovy -cp myjar.jar
USINGSYSTEMVARIABLES
#!/usr/bin/env groovy -cp ${HOME}/bin
DEFININGSYSTEMVARIABLES
#!/usr/bin/env VAR1=value1 VAR2=value2 groovy
BUT
DOESNOTWORKONLINUX
Alternative script for /usr/bin/env
Without defining system variables
#!/bin/bash
ARGS=( $1 ) # separate $1 into multiple space-delimited arguments.
shift # consume $1
PROG=`which ${ARGS[0]}` # find path for executable
unset ARGS[0] # discard executable name
ARGS+=( "$@" ) # remainder of arguments preserved "as-is".
# iterate array and expand variables
declare -a PARAMS
for i in "${ARGS[@]}"
do
PARAMS=("${PARAMS[@]}" "`eval "echo $i"`")
done
exec $PROG "${PARAMS[@]}" # execute the command
GROOVYSERV
FASTERSTARTUP
JVM process running in the background
Scriptstart ~10-20 times faster
INSTALLATION
WINDOWS
GroovyServ is part of the Groovy Windows-Installer
LINUX
gvm install groovyserv
MACOSX
brew install groovyserv
BINARYPACKAGE
$ cd /tmp
$ unzip groovyserv-1.0.0-bin.zip
$ groovyserv-1.0.0-bin/bin/setup.sh
export PATH=/tmp/groovyserv-1.0.0/bin:$PATH
USAGE
groovyclient MyScript.groovy
#!/usr/bin/env groovyclient
WRITING
SCRIPTS
RESTRICTIONS
Classname == Filename
to be found from other script
EXECUTING
SHELLCOMMANDS
"mkdir foo".execute()
["mkdir", "my directory"].execute()
WORKINGDIR&
ENVIRONMENT
"ls".execute([], new File("/tmp"))
"env".execute(["VAR1=Test", "VAR2=Something"], new File("."))
"env".execute([*:System.env,
VAR1: 'Test'].collect{ k,v -> "$k=$v" }, new File("."))
RESULTACCESS
println "ls".execute().text
println "cmd /c dir".execute().text
"ls".execute().inputStream.eachLine { println it }
"ls".execute().in.eachLine { println it }
"myCommand".execute().err.eachLine { println it }
def proc = new ProcessBuilder("myCommand").redirectErrorStream(true).start()
proc.in.eachLine { println it }
PROCESSCONTROL
Process process = "mkdir foo".execute()
process.waitFor()
int exitValue = process.exitValue()
if(!exitValue) {
//do your error-handling
}
if(!"mkdir foo".execute().waitFor()) {
//do your error-handling
}
"grep pattern".execute().waitForOrKill(1000)
def process = "myLongRunningCommand".execute()
...
process.destroy()
PROCESSOUTPUT
Process process = "myCommand".execute()
def out = new StringBuffer()
def err = new StringBuffer()
process.waitForProcessOutput( out, err )
if( out.size() > 0 ) println out
if( err.size() > 0 ) println err
def p = "rm -f foo.tmp".execute([], tmpDir)
p.consumeProcessOutput() // Prevent blocking by small buffer
p.waitFor()
PIPING
"less temp.sh".execute().pipeTo("grep Detected".execute()).text
def proc1 = "less temp.sh".execute()
def proc2 = "grep Detected".execute()
proc1 | proc2
println proc2.text
WILDCARDS
"ls *.java".execute() // won't work
"sh -c ls *.java".execute() // Shell resolves the wildcard
SHELLHELPERI
class Shell {
final Map env = System.env
File dir = new File('.')
boolean redirectErrorStream = false
long timeout = 5000
Shell env(Map env) {
this.env.putAll(env); return this
}
Shell dir(File dir) {
this.dir = dir; return this
}
Shell dir(String dir) {
this.dir = new File(dir); return this
}
Shell redirectErrorStream(boolean redirectErrorStream = true) {
this.redirectErrorStream = redirectErrorStream; return this
}
Shell timeout(int timeout = 5000) {
this.timeout = timeout; return this
}
SHELLHELPERII
Process execute(String command) {
new ProcessBuilder(['sh', '-c', command])
.directory(dir)
.redirectErrorStream(redirectErrorStream)
.environment(env.collect{k,v -> "$k=$v"} as String[])
.start()
}
int call(String command, boolean consumeOutput = true) {
Process proc = execute(command)
if(consumeOutput)
proc.consumeProcessOutput()
if(timeout)
proc.waitForOrKill(timeout)
else
proc.waitFor()
return proc.exitValue()
}
def eachLine(String command, Closure action) {
execute(command).in.eachLine(action)
}
}
SHELLHELPERIII
def shell = new Shell()
shell.call("mkdir /tmp/groovy")
shell.call("echo 123 >> /tmp/groovy/test")
shell.dir("/tmp/groovy").call("echo $HOME >> test2")
shell.eachLine("ls ."){
println "- $it -"
}
shell.eachLine("cat test2"){
println "- $it -"
}
HELPFULLSTUFF
ACCESSINGSYSTEM-VARIABLES
println System.env.PWD
ACCESSINGSYSTEM-PROPERTIES
println System.properties.'user.dir'
GETYOURPID
import java.lang.management.*
println ManagementFactory.runtimeMXBean.name.split('@').first().toInteger()
CLIBUILDER
PARSINGCOMMANDLINE
ARGUMENTS
DSL ontop of Apache Commons CLI
!#/usr/bin/env groovy
def cli = new CliBuilder(usage: 'MyScript')
cli.with {
v(longOpt: 'version', 'show version information')
}
def opt = cli.parse(args)
if (!opt)
System.exit(2)
if (opt.v) {
println "Version: 1.0.0"
}
ASAMPLE
usage: MyScript [options] [args]
-?,--help usage information
--config <arg> A script for tweaking the configuration
-D <property=value> use value for given property
-s,--source <path> Aliases for '-source'
-source <path> Specify where to find the files
-v,--version version information
THECODEI
#!/usr/bin/env groovy
def cli = new CliBuilder(usage: 'MyScript [options] [args]')
cli.with {
source (args:1, argName: 'path', optionalArg: false,
'Specify where to find the files')
_ (longOpt: 'config', args:1, argName: 'arg', optionalArg: false,
'A script for tweaking the configuration')
s (longOpt: 'source', args:1, argName:'path', optionalArg: false,
"Aliases for '-source'")
'?' (longOpt: 'help', 'usage information')
v (longOpt: 'version', 'version information')
D (args: 2, valueSeparator: '=', argName: 'property=value',
'use value for given property')
}
THECODEII
def opt = cli.parse(args)
if (!opt) // usage already displayed by cli.parse()
System.exit(2)
if(opt.'?')
cli.usage()
else if (opt.v)
println "Version: 1.0.0"
else {
if (opt.config)
println "Configuration: $opt.config"
if(opt.D) {
println "Custom properties:"
println opt.Ds.collate(2).collect{ it.join('=') }.join('n')
}
def home = System.properties.'user.dir'
if (opt.s || opt.source)
home = opt.s ?: opt.source
println "Working on files:"
opt.arguments().each println "$home/$it"
}
DEPENDENCY
MANAGEMENT
GRAPE
GROOVYADAPTABLEPACKAGINGENGINE
@Grab(group='org.springframework', module='spring-orm',
version='3.2.5.RELEASE')
import org.springframework.jdbc.core.JdbcTemplate
@Grab('org.springframework:spring-orm:3.2.5.RELEASE')
import org.springframework.jdbc.core.JdbcTemplate
Cachedirectory ~/.groovy/grape
@GRABANNOTATIONI
Annotatable everywhere
often seen at import statements
group (String)
Maven groupId or Ivy organization
module (String)
Maven artifactId or Ivy artifact
version (String)
literal
'1.1-RC3'
Ivy range
'[1.0, 2,0]' -> 1.0 or 2.0
'[2.1,)' -> 2.1 or greater
classifier (String)
'jdk15'
optional
MULTIPLEDEPENDENCIES
@Grapes([
@Grab(group='commons-primitives', module='commons-primitives',
version='1.0'),
@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')
])
class Example {
...
}
REPOSITORIES
Configuration in ~/.groovy/grapeConfig.xml
Grape cache -> ~/.groovy/grapes
Maven Local -> ~/.m2/repository
JCenter (includes Maven Central)
Codehaus.org
IBiblio
Java.net
http://jcenter.bintray.com
http://repository.codehaus.org
http://www.ibiblio.org
http://download.java.net/maven/2
@GrabResolver(name='restlet', root='http://maven.restlet.org/')
@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
EXCLUDETRANSITIVEDEPENDENCIES
@Grab('net.sourceforge.htmlunit:htmlunit:2.8')
@GrabExclude('xml-apis:xml-apis')
GRABCONFIG
@GrabConfig(systemClassLoader=true)
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
GrabConfig
systemClassLoader (boolean)
using the System-ClassLoader
initContextClassLoader (boolean)
ContextClassLoader = CL of the current thread
autoDownload (boolean)
automatic downloading
disableChecksums (boolean)
checking checksums
PROXY-CONFIGURATION
$ groovy -Dhttp.proxyHost=<host> -Dhttp.proxyPort=<port>
-Dhttp.proxyUser=<user> -Dhttp.proxyPassword=<user> yourscript.groovy
JAVA_OPTS = -Dhttp.proxyHost=<host> -Dhttp.proxyPort=<port>
-Dhttp.proxyUser=<user> -Dhttp.proxyPassword=<user>
QUESTIONS?
Alexander (Sascha) Klein
 
codecentric AG
Curiestr. 2
70563 Stuttgart
 
tel +49.711.67400-328
mobile +49.172.5294020
alexander.klein@codecentric.de
@saschaklein
 
http://www.codecentric.de
http://blog.codecentric.de

Groovy on the Shell