Schon gewusst? JDK enthält serienmäßig JavaScript Shell

Keine Kommentare

Wer arbeitet auf einem Mac oder Windows-Rechner und installiert seine Software anschließend auf einem Unix/Server?

Wer braucht ab und zu eine Kommandozeile mit Ad-Hoc-Programmierung?

Wer möchte nicht auf jedem System eine neue Shell-Sprache lernen?

Wer eine der obigen Fragen mit „ja“ beantwortet, für den könnte dieser Blog-Beitrag einige nützlichen Tipps enthalten, auch wenn die hier dargestellten Fakten nicht neu sind: Die JavaScript-Shell existiert bereits seit Java 6. (Das diesen Monat gerade seine Unterstützung durch Oracle verliert…)

Erste Schritte

Mit Java 6 hat Sun das Kommanto jrunscript eingeführt (nur im JDK, im JRE ist es nicht enthalten). Nach dem Start begrüßt es den Nutzer mit „js>“ und einem freundlich blinkenden Cursor. Der geneigte Linux-Nutzer tippt nun sicher ls, der Windows-Nutzer dir. Hier sind jedoch beide gleich schlecht dran, es funktioniert nicht wie erwartet. Wir arbeiten hier schließlich mit JavaScript, dort evaluiert der Name einer Funktion zu einem Funktions-Objekt. Will man die Funktion ausführen, so muss man sie aufrufen. Richtig sind daher die beiden folgenden Varianten: ls() oder dir(). Beide funktionieren auf allen Betriebssystemen und geben den Inhalt des aktuellen Verzeichnisses im Unix-Stil von „ls –l“ aus.

Das Kommando jrunscript besteht nur aus ein wenig Code um das Java Scripting API [JSR223]. Jede eingegebene Zeile wird als JavaScript-Ausdruck betrachtet und ausgewertet. Ist das Ergebnis des Ausdrucks nicht null, so wird es per toString() in eine lesbare Form verwandelt und auf der Konsole ausgegeben. Eine Shell wird daraus erst durch einen Satz von in JavaScript implementierten Funktionen. Es handelt sich dabei um leicht abgewandelte Varianten von häufig genutzten Unix-Kommandos wie „ls„, „cd„, „pwd“ etc.. Die komplette Liste ist von Oracle dokumentiert, siehe  [JRUNSCRIPT]. Neben den üblichen Verdächtigen existieren noch einige für eine Shell eher unübliche Kommandos, mit deren Hilfe man XML einlesen kann (in ein DOM) oder eine Xslt-Transformation anstoßen kann.

Bis jetzt haben wir die interaktive Betriebsart von jrunscript gesehen, natürlich sind aber auch Batches möglich: Die Option -f, gefolgt von einem Dateinamen, führt das in der Datei enthaltene JavaScript-Programm aus und terminiert anschließend.

Dies kann man für automatisch zu festen Zeiten gestartete Jobs nutzen. Unter Unix/Linux startet man solche Jobs typischerweise mittels „cron“, unter Windows heißt das Gegenstück „Geplante Tasks“ (in der Systemsteuerung suchen).

Der Vorteil gegenüber .bat-Dateien oder bash-Shellskripten ist die Unabhängigkeit vom Betriebssystem, da JavaScript überall läuft, wo auch Java läuft. Schaut man sich in seinem (Web-)Projekt um, wird man schnell jemand finden, der die notwendigen JavaScript-Kenntnisse besitzt. Oft reicht dafür schon ein Blick in den Spiegel. Heutzutage ist es oft schon schwieriger, jemand zu finden, der fließend bash spricht.

Ad Hoc Programmierung

In einer Programmiersprache sollte man Programme schreiben – und auch ausführen! jrunscript eignet sich insbesondere für kurze Wegwerfprogramme, die man nur ein- oder zweimal laufen lässt. Nehmen wir mal an, im Build-Script fehlt ein ordentliches Clean-Target, man möchte aber trotzdem rekursiv alle .class-Dateien löschen. Geht ganz einfach: find('.', '.*\.class', rm). Der erste Parameter der find-Funktion ist das Start-Vereichnis (Punkt steht für aktuelles Verzeichnis). Der zweite ist ein regulärer Ausdruck für den Dateinamen (ohne Pfad). Interessant wird’s beim dritten Parameter: Hier übergibt man eine Funktion, die beim Aufruf einen Parameter erhält, den absoluten Dateinamen der gefundenen Datei. Im Beispiel ist es die eingebaute Löschfunktion.

Zur Erinnerung: Callbacks in JavaScript sind Funktions-Objekte. Sie erhält man, indem man entweder nur den Funktionsnamen – ohne Klammern – angibt oder als anonyme Funktion. Nun zur Veranschaulichung ein weiteres Beispiel mit einer anonymen Funktion (Lambda-Ausdruck). Der Schnipsel findet alle Java-Dateien, deren absoluter Dateiname länger als 60 Zeichen ist. Damit kann man Dateien finden, die auf Betriebssystemen mit anderen Limits für Pfadlängen Ärger machen:

find('.', '.*\.java', function(x) { if (x.toString().length() > 60) echo(x) })

Leider hat die Shellkeine eingebaute Hilfe, man muss also entweder zum Browser greifen und die Dokumentation bei Oracle lesen oder man nutzt ein anderes Feature: echo(rm) gibt den JavaScript-Code der eingebauten Funktion rm aus. Den kompletten Sourcecode aller eingebauten Funktionen findet man im JDK im Archiv tools.jar, darin die Datei com/sun/tools/script/shell/init.js.

Jede eingetippte Zeile wird direkt ausgeführt, es wird nicht geprüft, ob der Ausdruck vollständig ist. Enthält sie nur einen unvollständigen Ausdruck oder endet mit einem unvollständigen Ausdruck, so antwortet jrunscript mit einem Syntax-Fehler. Es gibt aber einen Trick, seine Ausdrücke über mehr als eine Zeile hinweg einzugeben, mit Hilfe der beiden Funktionen read() und eval(): return eval(read('>', true)). Der erste Parameter von read ist der Prompt, der zweite gibt an, dass wir mehr als eine Zeile lesen wollen. Sobald read() eine leere Zeile liest, terminiert es.

Selbstegebaute Kommandos

Wie schon erwähnt, kann man mittels -f dateiname.js eine Datei mit JavaScript-Code ausführen. Die Option kann auch mehrfach genutzt werden, um mehrere Dateien in einem Rutsch auszuführen. Da -f - den Code von der Standardeingabe liest, lassen sich Batch und interaktive Arbeit auch kombinieren: Erst ein Skript ausführen, anschließend in den interaktiven Modus wechseln.

So hat man die Möglichkeit, den Namespace des Interpreters mit eigenen Funktionen und Objekten zu füllen. Für das oben erwähnte Multi-Line-Problem lässt sich einfach eine Funktion definieren:

function ml() { return eval(read('>', true)) }

Damit kann man interaktiv die Funktion ml() nutzen, wann immer man Kommandos auf mehr als einer Zeile eingeben möchte.

Zusammenfassung

jrunscript lässt sich schlecht mit „normalen“ Shells vergleichen. Die Syntax von JavaScript ist geschwätziger, man muss mehr Zeichen eingeben, zum Beispiel die Klammern für Funktionsaufrufe. Kommandos lassen sich dagegen in anderen Shells nur durch den Namen aufrufen. In JavaScript benötigt man kein Dollar- oder Prozent-Zeichen vor Variablennamen, dafür müssen Strings in Anführungszeichen eingeschlossen sein. Das hat auch Vorteile: Wie oft hat man bei Unix Ärger, weil in einem Dateinamen ein Leerzeichen enthalten ist?

Interessant ist auch der Vergleich mit einer JavaScript-Konsole im Browser. Hier ist die Sprache identisch, aber der Kontext komplett anders: Im Browser hat man Objekte wie window, in jrunscript dagegen durch die Java-Bridge vollen Zugriff auf alle Klassen des JDK (siehe auch [JSGUIDE] für Details). Da keine Sandbox greift, darf man aus den Skripten heraus auch auf das Dateisystem zugreifen. Mit zusätzlichen jar-Dateien im Klassenpfad lassen sich die Möglichkeiten sogar noch erweitern.

Die Shell ist sicher kein Ersatz für Build-Tools wie ant, maven oder gradle, dafür ist sie jedoch immer schnell zur Hand, wenn man etwas Logik ausführen muss und es sich nicht lohnt, den Java-Compiler anzuwerfen. In diesem Sinne mag es beim Entwickeln eine kleine Nische füllen.

Werbung

Bei jrunscript handelt es sich nur um einen kleinen Wrapper um das Java-Scripting-API. Wer dazu und zu anderen Themen wie Rhino oder der JavaScript-Schnittstelle in ant mehr wissen möchte, dem kann ich das eBook JavaScript auf dem Server empfehlen.

Ach ja: Wenn man von jrunscript genug hat: quit()

Roger Butenuth

Dr. Roger Butenuth hat in Karlsruhe Informatik studiert und anschließend in Paderborn promoviert (Kommunikation in Parallelrechnern). Er hat langjährige Erfahrung in der Projekt- und Produktentwicklung.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.