Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Groovy on the shell

513 views

Published on

Slides from my talk Groovy on the shell at Gr8conf EU 2017

Published in: Software
  • Be the first to comment

  • Be the first to like this

Groovy on the shell

  1. 1. Groovy on the shell_ Alexander (Sascha) Klein <alexander.klein@codecentric.de>   1
  2. 2. Why ?   2
  3. 3. Groovy is cool   3
  4. 4. Because I can   4
  5. 5. To do complex stuff   5
  6. 6. Testing your scripts   6
  7. 7. If you know Groovy better than Bash   7
  8. 8. About me Alexander Klein Branchmanager codecentric AG Curiestr. 2 70563 Stuttgart, Germany +49 (0) 172 529 40 20 alexander.klein@codecentric.de www.codecentric.de blog.codecentric.de @saschaklein   8
  9. 9. Executing Groovy scripts $ groovy Test.groovy   9
  10. 10. She-Bang Comment (#!) #!/usr/local/bin/groovy   10
  11. 11. Using the default environment Defining the classpath Using system variables Defining system variables #!/usr/bin/env groovy #!/usr/bin/env groovy -cp myjar.jar #!/usr/bin/env groovy -cp ${HOME}/bin #!/usr/bin/env VAR1=value1 VAR2=value2 groovy   11
  12. 12. But Does not work on Linux 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   12
  13. 13. 'Hacks' for the rescue #!/bin/bash //usr/bin/env /path/to/groovy "$0" $@; exit $? println "My groovy script" #!/bin/bash //usr/bin/true && export CLASSPATH=.... //usr/bin/true && export OPTS="-Dmy.opt1=foo -Dmy.opt2=bar" //usr/bin/true && export OPTS="$OPTS -Dmy.opt3=foobar" //path/to/groovy $OPTS "$0" $0; exit $? println System.env[my.opt1]   13
  14. 14. 'Hacks' for the rescue #!/bin/bash REM=''' ' # bash stuff here export export CLASSPATH=.... export OPTS="-Dmy.opt1=foo -Dmy.opt2=bar" export OPTS="$OPTS -Dmy.opt3=foobar" /path/to/groovy $OPTS "$0" $@ exit $? ' ''' // groovy stuff here println "My groovy script"   14
  15. 15. A solution for Windows @ECHO OFF REM = / REM dummy groovy statement in first line with an REM assignment to a dummy var called REM SET _JAVA_OPTIONS= SET CLASSPATH= C:pathtogroovy "%~dp0%~nx0" %* GOTO :EOF / // dummy groovy annotation to make groovy compiler // to ignore the first line @interface ECHO {} // groovy stuff here println "My groovy script"   15
  16. 16. GroovyServ Faster Startup JVM process running in the background Scriptstart ~10-20 times faster   16
  17. 17. Installation Windows GroovyServ is part of the Groovy Windows-Installer Linux Mac OS X Binary package curl -s "https://get.sdkman.io" | bash sdk install groovyserv brew install groovyserv $ cd /tmp $ unzip groovyserv-1.1.0-bin.zip $ groovyserv-1.1.0-bin/bin/setup.sh export PATH=/tmp/groovyserv-1.1.0/bin:$PATH   17
  18. 18. Usage $ groovyclient MyScript.groovy #!/usr/bin/env groovyclient   18
  19. 19. Writing Scripts   19
  20. 20. Restrictions Classname == Filename to be found from other script   20
  21. 21. Executing shell commands "mkdir foo".execute() ["mkdir", "my directory"].execute()   21
  22. 22. 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("."))   22
  23. 23. Result access 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 }   23
  24. 24. Process control 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()   24
  25. 25. Process output 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()   25
  26. 26. 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   26
  27. 27. Wildcards "ls *.java".execute() // won't work "sh -c ls *.java".execute() // Shell resolves the wildcard   27
  28. 28. Shell Helper I 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 }   28
  29. 29. Shell Helper II 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) } }   29
  30. 30. Shell Helper III def shell = new Shell() shell.call("mkdir /tmp/groovy") shell("echo 123 >> /tmp/groovy/test") shell.dir("/tmp/groovy").call("echo $HOME >> test2") shell.eachLine("ls ."){ println "- $it -" } shell.eachLine("cat test2"){ println "- $it -" }   30
  31. 31. Helpfull stuff Accessing System-Variables Accessing System-Properties Get your PID println System.env.PWD println System.properties.'user.dir' import java.lang.management.* println ManagementFactory.runtimeMXBean.name.split('@').first()   31
  32. 32. CliBuilder   32
  33. 33. Parsing Commandline 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" }   33
  34. 34. A Sample 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   34
  35. 35. The code I #!/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') }   35
  36. 36. The code II 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" }   36
  37. 37. Questions? Alexander Klein Branchmanager codecentric AG Curiestr. 2 70563 Stuttgart, Germany +49 (0) 172 529 40 20 alexander.klein@codecentric.de www.codecentric.de blog.codecentric.de @saschaklein   37
  38. 38. Dependency Management   38
  39. 39. Grape Groovy Adaptable Packaging Engine Cachedirectory ~/.groovy/grape @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   39
  40. 40. @Grab Annotation I 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   40
  41. 41. Multiple Dependencies @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 { ... }   41
  42. 42. Repositories Configuration in ~/.groovy/grapeConfig.xml Grape cache → ~/.groovy/grapes Maven Local → ~/.m2/repository JCenter (includes Maven Central) IBiblio Java.net http://jcenter.bintray.com 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')   42
  43. 43. Exclude transitive dependencies @Grab('net.sourceforge.htmlunit:htmlunit:2.8') @GrabExclude('xml-apis:xml-apis')   43
  44. 44. GrabConfig GrabConfig systemClassLoader (boolean) using the System-ClassLoader initContextClassLoader (boolean) ContextClassLoader = CL of the current thread autoDownload (boolean) automatic downloading disableChecksums (boolean) checking checksums @GrabConfig(systemClassLoader=true) @Grab(group='mysql', module='mysql-connector-java', version='5.1.6')   44
  45. 45. 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>   45

×