Groovy Shell Scripting Teil 1 - Java++ Für Die Shell

Groovy ist eine dynamische Sprache auf der JVM und glänzte schon einige Jahre vor Java mit Features wie funktionaler Programmierung oder Literals für oft benutzte Datenstrukturen wie List und Map.

Groovy ist eigentlich eine universal einsetzbare Programmiersprache, die man z.B. oft bei Banken findet. Was mich aber damals zu Groovy geführt hat war das Problem, dass meine in einen Tomcat deployte Anwendung keine HTTPS-Verbindung zu einem anderen System aufmachen konnte, obwohl Firewall und curl keine Probleme damit hatten. Letztendlich habe ich das Problem gefunden, indem ich ein Groovy-Skript mit der gleichen JVM wie der Tomcat gestartet habe. Es fehlten Zertifikate im Trust Store der JVM.

Später musste ich Daten aus einer komplexeren XML-Datei extrahieren oder Automatisierungsskripte schreiben und landete wieder bei Groovy.

Jetzt werdet ihr euch sicher fragen, ob es ein großer Umstieg war, gleich eine neue Sprache zu lernen. Nein, als Java-Programmierer hatte ich nie das Gefühl, eine wirklich neue Sprache lernen zu müssen. Groovy war eher Java wie ich es mir immer gewünscht hatte. Wenn man sich neuere Sprachfeatures von Java ansieht, etwa die Type Inference in Java 10 oder die in Java 8 eingeführten Lamdas, muss man anerkennen, dass Groovy viele Trends schon erkannt hatte, bevor sie auch nur in die Nähe von Java gekommen sind.

Ich bin also über das Skripting von Java zu Groovy gekommen. In diesem und den nächsten Blogbeiträgen möchte ich euch einige Features beschreiben, die ich oft verwende, wenn ich mit Groovy auf der Shell unterwegs bin.

##Getting Groovy - Installation und Setup Meinen liebsten Installationsweg für Groovy habe ich gefunden, als ich ein Tool gesucht habe, um zwischen Versionen meiner JVM umschalten zu können. Gelandet bin ich bei sdkman, einem CLI-Tool, mit dem man eine Menge SDKs installieren und zwischen ihren Versionen hin- und herschalten kann.

Die Installation ist denkbar einfach:

curl -s "https://get.sdkman.io" | bash

Nach der Installation seht ihr in eurer .bashrc die folgende Zeile:

# THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

Wenn ihr die zsh, vielleicht sogar mit oh-my-zsh verwendet, wie es [Roland schon beschrieben hat](TODO link) könnt ihr diese Zeilen auch einfach ans Ende eurer .zshrc kopieren und alles funktioniert. Öffnet einmal eine neue Shell und ihr könnt loslegen:

➜  ~ sdk list groovy
================================================================================
Available Groovy Versions
================================================================================
     3.0.0-alpha-3       2.3.11              2.0.8               1.7.5
     3.0.0-alpha-2       2.3.10              2.0.7               1.7.4
     3.0.0-alpha-1       2.3.9               2.0.6               1.7.3
     2.6.0-alpha-4       2.3.8               2.0.5               1.7.2
     2.6.0-alpha-3       2.3.7               2.0.4               1.7.1
     2.6.0-alpha-2       2.3.6               2.0.3               1.7.0
     2.6.0-alpha-1       2.3.5               2.0.2               1.6.9
 > * 2.5.1               2.3.4               2.0.1               1.6.8
     2.5.0               2.3.3               2.0.0               1.6.7
   * 2.4.15              2.3.2               1.8.9               1.6.6
     2.4.14              2.3.1               1.8.8               1.6.5
     2.4.13              2.3.0               1.8.7               1.6.4
     2.4.12              2.2.2               1.8.6               1.6.3
     2.4.11              2.2.1               1.8.5               1.6.2
     2.4.10              2.2.0               1.8.4               1.6.1
     2.4.9               2.1.9               1.8.3               1.6.0
     2.4.8               2.1.8               1.8.2               1.5.8
     2.4.7               2.1.7               1.8.1               1.5.7
     2.4.6               2.1.6               1.8.0               1.5.6
     2.4.5               2.1.5               1.7.11              1.5.5
     2.4.4               2.1.4               1.7.10              1.5.4
     2.4.3               2.1.3               1.7.9               1.5.3
     2.4.2               2.1.2               1.7.8               1.5.2
     2.4.1               2.1.1               1.7.7               1.5.1
     2.4.0               2.1.0               1.7.6               1.5.0

================================================================================
+ - local version
* - installed
> - currently in use
================================================================================

Ihr seht am *, dass ich gerade Groovy 2.5.1 (die derzeit aktuelle stable Version) und 2.4.15, die aktuelle Version der 2.4er Reihe installiert habe. Am > seht ihr, dass 2.5.1 auch meine default Version für Groovy ist.

Will ich jetzt die aktuelle Alphaversion von Groovy 2.6 installieren, mache ich das mit:

➜ ~ sdk install groovy 2.6.0-alpha-4

Die Version für die aktuelle Shell setze ich mit

➜ ~ sdk use groovy 2.5.1

und den Default für alle Shells mit

➜ ~ sdk default groovy 2.5.1

sdkman hat noch weitere interessante Features, aber das ist ein Thema für einen anderen Blogpost.

A groovy kind of Java - Erste Schritte

Der erste Schritt mit einer neuen Programmiersprache ist in unserer Zunft das “Hello World”. In der Einführung habe ich schon erwähnt, dass ich fast keine Umgewöhnung zwischen Groovy und Java hatte. Das liegt daran, dass Groovy als syntaktische Obermenge von Java entwickelt wurde. Das bedeutet, dass Ihr HelloWorld.java in Java schreiben könnt und es ist valides Groovy. Fangen wir genau so an:

public class Hello {
    public static void main(String [] args) {
        System.out.println("Hello World!");
    }
}

Das würde in Java kompilieren und laufen. Mit Groovy kann ich es auch einfach so ausführen. Ich muss nur die Dateiendung ändern:

➜  mv HelloWorld.java HelloWorld.groovy
➜  groovy HelloWorld.groovy
Hello World!

Es geht aber noch besser. Groovy hat einige Verbesserungen an der Syntax vorgenommen, die die Lesbarkeit von Skripten oft erheblich erhöht, z.B.

  • das Semikolon am Zeilenende kann entfallen
  • man braucht kein public mehr an Klassen und Methoden, das ist der Default
  • man kann Klammern weglassen
  • es gibt viele automatisch importierte Klassen und statische Methoden

Die ganzen Features sind wieder ein Thema für einen eigenen Blogpost. Stay tuned…

Für uns auf der Shell ist wichtig, dass man sogar noch die Klassendeklaration weglassen kann. Man bekommt automatisch eine Unterklasse von groovy.lang.Script. Es bleibt folgendes von unserer ehemaligen Klasse übrig:

System.out.println("Hello World!");

Die eben angesprochen automatischen Imports z.B. von println und die optionalen Semikolons machen es zu

println("Hello World!")

und auch die Klammern bei Methodenaufrufen sind optional. Es bleibt ein Einzeiler:

println "Hello World!"

Um unser Skript zu einem first class citizen auf der Shell zu machen, muss es sich in die Unix-Philosophie der kleinen komponierbaren Programme einfügen. Dazu muss es wie andere Skripte ausführbar sein und Daten über Pipes von anderen Programmen annehmen.

Ausführbarkeit bekommen wir, indem wir eine Shebang an den Anfang unseres Skriptes einfügen. Damit sagen wir unserer Shell, dass sie einen neuen Prozess mit dem hinter dem Shebang angegebenen Programm starten soll:

#!/usr/bin/env groovy
println "Hello World!"

Danach müssen wir die Datei noch ausführbar machen:

➜  chmod +x HelloWorld.groovy
➜  ./HelloWorld.groovy
Hello World!

Um gänzlich zu verbergen, dass wir es mit Groovy zu tun haben, können wir noch die Dateiendung entfernen. Groovy braucht sie nicht mehr, wenn unser Skript mit einer Shebang anfängt.

➜  mv HelloWorld.groovy hello_world
➜  ./hello_world
Hello World!

Im nächsten Teil stelle ich weitere Features von Groovy auf der CLI vor, etwa wie man mit Pipes Daten von anderen Programmen weiter verarbeitet.