DrDialog, oder wie ich lernte, REXX zu lieben - Teil 8

Von Thomas Klein © Mai 2003

Als ich ursprünglich die Idee zu dieser Artikelreihe hatte, wollte ich die Anfänger erreichen - auch die REXX-Anfänger. Bis auf einige kleine Ausnahmen haben wir bisher aber nur Objekte und Methoden in DrDialog besprochen und REXX selbst eigentlich außer Acht gelassen. Heute werden wir daher den "GUI-Kram" einmal vergessen und eine Einführung in das "reine" REXX durchziehen. Wenn Sie also kein OS/2-REXX-Neuling sind, dürfen Sie diese Ausgabe gerne überspringen. ;-)

Wo anfangen?

Im heutigen Teil schauen wir uns an, was es an Besonderheiten hinsichtlich Variablen und einigen grundlegenden Befehlen und Funktionen in REXX zu beachten gibt. Um das Erstellen von Skripten zu vermeiden, die lediglich aus einer oder zwei Zeilen Code bestehen um REXX' Verhalten zu testen, verwenden wir ein großartiges Hilfsprogramm zum "Spielen" mit REXX: Es nennt sich REXXTRY und eigentlich müßte es auch auf Ihrem System direkt über Befehlszeileneingabe startbar sein. Probieren Sie's mal aus - öffnen Sie eine Befehlszeilensitzung (ein OS/2-Fenster) und geben Sie rexxtry ein. Falls Sie Ulrich Möllers XWorkplace zusammen mit dem XCenter verwenden, können Sie natürlich auch den Menüpunkt Ausführen... im XCenter-Button verwenden und dort rexxtry eingeben. In beiden Fällen sollten Sie danach etwas sehen, was so aussieht wie...:

rexxtry screen shot
Abb.1: Das rexxtry-Fenster

Wenn rexxtry nicht startet, müssen Sie prüfen, ob es sich in einem Verzeichnis befindet, das Teil der PATH-Variable Ihrer CONFIG.SYS ist. Oder - um es anders zu formulieren - Sie müssen es dann aus dem Verzeichnis heraus starten, in dem es sich befindet. Suchen Sie nach einer Datei namens rexxtry.cmd. Standardmäßig sollte sie sich im Verzeichnis \OS2 auf dem OS/2-Installationslaufwerk befinden.
Rexxtry kann auf unterschiedliche Arten verwendet werden, je nach dem wie es gestartet wurde. In unserem Beispiel verwenden wir beim Aufruf keinerlei Parameter und somit startet es im "interaktiven Modus", in dem jede Eingabezeile einzeln verarbeitet (interpretiert) wird.
Um rexxtry zu beenden, geben Sie einfach exit ein. Übrigens: Wie immer bei Befehlszeilen werden auch hier die Eingaben jeweils durch Drücken der Eingabetaste abgeschlossen...

Sag' ich nun hallo oder "hallo"?

Um mit einer kleinen Besonderheit zu REXX zu beginnen, versuchen wir 'mal, unser Programm "Hallo" sagen zu lassen, indem wir den say-Befehl verwenden.
say wird verwendet, um "einen Ausdruck in den Standard-Ausgabedatenstrom zu schreiben". Da wir den interaktiven Modus von rexxtry verwenden und keine zusätzlichen Tricks und Kniffe benutzen, ist der "Standard-Ausgabedatenstrom" in unserem Fall schlichtweg der Bildschirm. Wir zeigen also etwas an. Was aber hat es nun mit "Ausdruck" auf sich? Um es einfach zu halten, lassen Sie es mich so sagen: Das kann entweder ein Literal sein (eine konstante Zeichenkette in Anführungszeichen), eine Variable oder eine Funktion. In den beiden letzten Fällen wird also entweder der Inhalt der Variable bzw. der Rückgabewert aus der Funktion ausgegeben.
Um endlich 'mal etwas zu tun, sagen wir Hallo, indem wir in rexxtry eingeben:

say "hallo"

Als Ergebnis erhalten wir eine einzelne Ausgabezeile mit dem Inhalt hallo und eine gepunktete Trennzeile, die rexxtry immer nach dem Abarbeiten einer Eingabe ausgibt (und die wir im Folgenden ignorieren werden).
Was das Ganze nun so besonders macht, ist die Tatsache, daß REXX's Interpreter ein echt hilfsbereiter Kerl ist. Würden wir statt des obigen Befehls eingeben...

say hallo

dann würde das bedeuten: Zeige den Inhalt der Variable hallo an - da wir nämlich keine Anführungszeichen verwenden. Eigentlich würde man nun eine Meldung erwarten, die darüber informiert, daß es keine Variable dieses Namens gibt. Das stimmt zwar, aber... REXX stellt diese Variable automatisch zur Verfügung und initialisiert sie direkt noch mit ihrem Namen (in Großbuchstaben) als Inhalt und gibt daher aus:

HALLO

Ups! Das ist das Besondere an der Sache... natürlich sieht die Sache anders aus, wenn wir selbst eine Variable namens hallo definieren. Geben Sie zuerst ein:

hallo = "Tach!"

Und dann:

say hallo

...und Sie werden sehen, daß nun folgendes ausgegeben wird:

Tach!

Denn nun existiert bereits eine Variable namens hallo zum Zeitpunkt des say-Befehls und REXX gibt deren Inhalt aus (welcher Tach! lautet).
Vielleicht haben Sie schon mit anderen Programmiersprachen gearbeitet und wissen daher, daß alle ein unterschiedliches Verhalten beim Formatieren relativ einfacher Ausgaben besitzen, besonders wenn die Ausgaben aus einer Kombination von Literalen und Variablen bestehen. REXX bildet da keine Ausnahme - allerdings ragt es aus der Masse hervor, weil es sich extrem präzise verhält, wenn es um das geht, was "hinter" dem say-Schlüsselwort steht. Machen wir doch 'mal weiter und geben ein:

sdl = 42
say "Der Sinn des Lebens ist:" sdl

Daraus wird:

Der Sinn des Lebens ist: 42

Sie fragen sich jetzt wohl, was daran so besonders ist... nun, da steht doch eine Leerstelle zwischen dem Doppelpunkt und der 42. Haben wir gesagt, daß die da hin soll? Ich befürchte ja - um es zu verdeutlichen, geben Sie ein:

say "Der Sinn des Lebens ist:"sdl

Daraus wird nun:

Der Sinn des Lebens ist:42

Und nun - um ein letztes Beispiel anzugeben - geben Sie noch ein:

say "Der Sinn des Lebens ist:"       sdl

(mit 7 Leerstellen zwischen dem Literal und dem Variablennamen) und ausgegeben wird entsprechend:

Der Sinn des Lebens ist:       42

Die Anzahl der Leerstellen in say-Befehl entspricht genau der, die in der Ausgabe verwendet wird, was nicht gerade typisch für Programmiersprachen ist, soweit ich weiß. Normalerweise wird eine Anweisung untersucht und in Schlüsselwort und Parameter zerlegt, wobei die Anzahl der Leerstellen unerheblich ist.

Da wir nun also schon eine Methode kennen, um Ausgaben in REXX zu erzeugen, schauen wir uns doch noch einen Befehl an, um Eingaben zu verarbeiten. Wir verwenden dafür den Befehl PULL. Genau genommen dient PULL dazu, eine Zeile aus dem Standard-Eingabedatenstrom zu holen. Da wir in unserer rexxtry-Sitzung weder mit Warteschlangen noch mit Datenströmen herumgespielt haben, wird REXX dadurch veranlaßt, auf die Eingabe einer Zeile (Eingabe wird abgeschlossen mittels Eingabetaste) durch den Anwender zu warten. Geben Sie 'mal ein:

pull eingabe

Ja. Da wartet rexxtry nun auf Ihre Eingabe. Tippen Sie ruhig irgendeinen Satz ein, der Ihnen gerade durch den Kopf geht - Hauptsache, er besteht (zumindest teilweise) aus Kleinbuchstaben, denn das wird gleich interessant... nach Ihrer Eingabe geben Sie folgenden Befehl ein:

say eingabe

Und nun sehen Sie sich das an! Da hat der doch tatsächlich alles in Großbuchstaben umgewandelt! Aber so ist es nun mal - wenn Sie die Eingabe genau so benötigen, wie Sie gemacht wurde, dann müssen Sie eine andere Funktion namens PARSE in Kombination mit PULL verwenden, so wie in...

parse pull eingabe

Jetzt wird nach

say eingabe

genau das ausgegeben, was auch eingegeben wurde. Natürlich gibt es zu PARSE noch einiges mehr zu erzählen, aber das werden wir uns in einer späteren Ausgabe antun.

Nun kennen wir also schon das Grundgerüst, um Eingaben und Ausgaben in REXX zu erzeugen. Leider nützt uns das aber innerhalb DrDialog nichts, denn dessen Umgebung ermöglicht den Einsatz der oben beschrieben Funktionen für Ein-/Ausgabezwecke nicht - andererseits: Wozu sollte das gut sein? In DrDialog werden Ausgaben gemacht, indem die Texteigenschaft eines Objektes verändert wird und Eingaben werden durch das Auswerten eines Texteingabefelds erreicht. Der say-Befehl ist zwar in DrDialog sehr nützlich, um den Programmablauf zu kontrollieren (da seine Ausgaben im Fenster des Laufzeit-Monitors landen), aber sowohl say als auch pull ermöglichen Ihnen, auch außerhalb DrDialog einfache Skripte zu erstellen.
Wie dem auch sei: Für's erste bleiben wir lieber bei REXX-Elementen, die Sie auch in DrDialog einsetzen können. Schauen wir uns doch 'mal an, wie mathematische Berechnungen in REXX gemacht werden.

Einmaleins in REXX

In REXX werden mathematische Berechnungen einfach durch Angabe der entsprechenden Formel gemacht, wobei die übliche Notation mit PLUS, MINUS, STERN (für Multiplikation) und SLASH (der "normale" Schrägstrich für Division) verwendet wird. Wie üblich gelten auch hierbei die Standardregeln der Algebra - das heißt, die Berechnung erfolgt von links nach rechts, wobei Klammerausdrücke Vorrang haben und die aus der Schulzeit bekannte Regel "Punktrechnung geht vor Strichrechnung" gilt. Ein paar Beispiele:

Beachten Sie, daß einige der obigen Beispiele als Teil eines größeren Kontext zu verstehen sind: Wenn Sie beispielsweise den Wert einer Variablen um 1 erhöhen wollen, muß der Inhalt der Variablen numerisch sein, sonst erhalten Sie eine Fehlermeldung. Dasselbe gilt auch für das Beispiel ergebnis = a - 5: der Inhalt von a muß numerisch sein, bevor diese Anweisung erfolgt. In rexxtry würden Sie zum Beispiel folgendes eingeben:

cents = 3456
euros = cents / 100
say euros

...was dann 34.56 ausgeben würde.
Für die Deutschen Leser muß hier noch darauf hingewiesen werden, daß Nachkommastellen mittels eines Punkts vom ganzzahligen Teil getrennt werden, wie es in den USA üblich ist... das gilt auch für eigene Angaben von Nachkommawerten im Programm. Sie müssen einen Punkt verwenden.

Exponentialrechnung erfolgt durch Angabe eines doppelten Sterns, wie z.B. in:

say "Der maximale dezimale Wert eines Bytes ist" 2 ** 8  -1

Anführungszeichen und Kommentare

Schauen wir uns erst einmal die Kommentare an. Kommentare werden verwendet, um Informationen innerhalb eines Programms zu speichern, die keinen ausführbaren Code darstellen, also vom Interpreter oder Compiler nicht verarbeitet werden sollen. Damit werden einzelne Teile eines Programms dokumentiert, um ein besseres Verständnis für den Verarbeitungsablauf des Programms zu gewährleisten. Hinsichtlich der Kommentare gibt es eine Besonderheit in REXX - nämlich, daß ein REXX-Programm (oder Skript) mit einem Kommentar beginnen muß. Ich habe 'mal irgendwo gelesen, daß OS/2 diese Eigenschaft dazu verwendet, zwischen einem REXX-Skript und einer Batch-Datei (Stapeldatei) unterscheiden zu können, bin mir aber nicht sicher ob das stimmt.

Testüberschrift untere Ebene

Egal - nehmen Sie's einfach so hin: Ein REXX-Skript muß mit einem Kommentar beginnen. (In DrDialog ist das übrigens anders, da alles, was Sie dort codieren, innerhalb eines Code-Rahmens stattfindet, den die Entwicklungsumgebung zur Verfügung stellt.) Ein Kommentar beginnt mit der Zeichenfolge /* (normaler Schrägstrich und Stern) und endet mit der umgekehrten Zeichenfolge */ (Stern Schrägstrich). Ein einfacher Kommentar würde also beispielsweise so aussehen:

/* das ist ein Kommentar */

Kommentare können sich auch über mehrere Zeilen erstrecken - das könnte dann wie folgt aussehen:

/*
Die folgende Routine wird für jede Zeile aufgerufen,
die aus der Eingabedatei gelesen wird. Jede Zeichenkette
innerhalb der Zeile, die zu dem angegebenen Suchbegriff
paßt, wird entfernt und der Rest wird dann als eine
Zeile in die Ausgabedatei geschrieben. Die Anzahl der
gefundenen Zeichenketten in der Zeile wird als return
code an die aufrufende Routine zurückgegeben.
*/

Wenn sich ein Kommentar über mehrere Zeilen erstreckt, kann es mühsam werden, auf den ersten Blick Code von Kommentar zu unterscheiden. Daher werden Sie - meistens - längere Kommentare in REXX-Skripten in einer anderen Form vorfinden. Programmierer bevorzugen meist eine Abfolge von einzelnen Kommentarzeilen wie...:

/* die folgende Routine wird für jede Zeile aufgerufen,     */
/* die aus der Eingabedatei gelesen wird. Jede Zeichenkette */
/* innerhalb der Zeile die zu dem angegebenen Suchbegriff   */
/* paßt wird entfernt und der Rest wird dann als eine       */
/* Zeile in die Ausgabedatei geschrieben. Die Anzahl der    */
/* gefundenen Zeichenketten in der Zeile wird als return    */
/* code an die aufrufende Routine zurückgegeben.            */

Der Grund hierfür liegt einfach in der besseren Lesbarkeit solcher Kommentare: Man erkennt diesen Block sofort als einen "Kommentar" verglichen mit einem mehrzeiligen Textblock, der lediglich eine einleitende Kommentarkennung hat und die unter Umständen erst nach Blättern im Code gefunden und erkannt werden kann. Kommentare müssen übrigens auch nicht alleine auf einer Zeile stehen. Sie werden es bei kurzen Kommentaren bestimmt bevorzugen, diese mit dem Code zusammen auf einer Zeile zu schreiben, wie in folgendem Beispiel:

euro = dm / 1.95583  /* konvertiere DM-Betrag in Euro */

Und nun kommen wir zu den Anführungszeichen. REXX unterstützt sowohl einfache als auch doppelte Anführungszeichen als gleichbedeutend. Die beiden folgenden Anweisungen sind daher auch gleichbedeutend:

say "hallo"
say 'hallo'

Das einige, das Sie dabei beachten müssen ist, daß Sie für zusammengehörende Anführungszeichen auch denselben Typ (einfach/doppelt) verwenden, sonst erkennt REXX das nicht und beschwert sich mit der Meldung "unmatched quote or *". Das würde beispielsweise passieren bei einer Anweisung wie...:

say 'hallo"

REXX' Fähigkeit, beide Typen von Anführungszeichen zu unterstützen, ist dann sehr nützlich, wenn der zu verarbeitende (anzuzeigende) Text selbst Anführungszeichen enthält:

say "Die angegebene Datei gibt's nicht!"

oder

say 'Mein Chef sagt: "Wir müssen unsere Kernkompetenz stärker fokussieren." '

Doch selbst mit einem verständnisvollen Interpreter stößt man irgendwann auf eine Situation, in der im Text selbst dasselbe Anführungszeichen enthalten ist, das auch zur Eingrenzung des Textes selbst verwendet wird. Im Normalfall passiert das dann, wenn der Text beide Typen von Anführungszeichen verwendet. In REXX können Sie das dennoch bewerkstelligen, indem das Anführungszeichen im Text gedoppelt wird... Hmm.. es wird unübersichtlich. Hier ein Beispiel:

say 'Das Original von "That''s just the way it is." stammt aus Bruce Hornsby''s Feder und 2PAC hat''s gecovert.'

Ich schlage vor, Sie spielen ein wenig in rexxtry herum um selber ein Gespür für das Thema "Anführungszeichen" zu entwickeln.

Datum Zeit Beschreibung
Mai
04. Mai 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
05. Mai 2003 8:00PM EDT (01:00 GMT) Allgemeine VOICE Sitzung im IRC in #voice. Alle sind herzlich eingeladen.
07. Mai 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
11. Mai 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
14. Mai 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
18. Mai 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
19. Mai 2003 8:00PM EDT (01:00 GMT) Allgemeine VOICE Sitzung im IRC in #voice. Alle sind herzlich eingeladen.
21. Mai 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
25. Mai 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
28. Mai 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
Juni
01. Juni 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
02. Juni 2003 8:00PM EDT (00:00 GMT) Allgemeine VOICE Sitzung im IRC in #voice. Alle sind herzlich eingeladen.
04. Juni 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
08. Juni 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
11. Juni 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
15. Juni 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
16. Juni 2003 8:00PM EDT (00:00 GMT) Allgemeine VOICE Sitzung im IRC in #voice. Alle sind herzlich eingeladen.
18. Juni 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
22. Juni 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
25. Juni 2003 10:00PM EDT (02:00 GMT) Der SCOUG Help Desk trifft sich jeden Mittwoch im Kanal #SCOUG des WEBBnet.
29. Juni 2003 3:00PM EDT (19:00 GMT) Das Warp Doctor-Team trifft sich jeden Sonntag im Kanal #warpdoctor des WEBBnet.
Oktober
18. Oktober 2003 8:00AM EDT (12:00 GMT) Warpstock 2003 am Wochenende des 18./19. Oktober in San Francisco, Kalifornien. Weitere Informationen unter http://www.warpstock.org.

Und was kommt dann?

Tja, für heute wär's das erst mal. In der nächsten Ausgabe schauen wir uns REXX' Konstrukte für bedingte Ausführung (if, select), Iterationen (Schleifen), Unterroutinen und Funktionen an. In Teil 10 kümmern wir uns um die Fülle von Funktionen, die REXX zur Zeichenkettenverarbeitung bietet, und nehmen uns auch einige der RexxUtils-Funktionen vor. Danach, in Teil 11 kommen wir zurück zu DrDialog und lernen, wie man mit mehreren Dialogen innerhalb eines Programms umgeht. Was danach kommt weiß ich noch nicht so genau - entweder beginnen wir dann mit unserer größeren Beispielapplikation oder wir schieben noch einen Teil mit "vergessenen" Punkten dazwischen... mal sehen. Bleiben Sie dran!

Daten und Quellen:

GuiObjectREXX Yahoo!-Gruppe: http://groups.yahoo.com/group/GuiObjectREXX/
Newsgruppe zur GUI-Programmierung mit REXX: news://news.consultron.ca/jakesplace.warp.visualrexx
Download von Hobbes: http://hobbes.nmsu.edu/cgi-bin/h-search?key=drdialog&pushbutton=Search
Code-Beispiel: http://de.os2voice.org/VNL/past_issues_DE/VNL0303H/cntsamp2_de.zip

Thomas Klein ist langjähriger Mitstreiter und Mitglied des Übersetzungsteams.