EFEU Programmierung E rühstück rich E F Erich Frühstück ntwicklungs U mgebung Programmentwicklung mit EFEU Vorwort Dieses Buch wendet sich an Programmentwickler und Anwender unter UNIX, die für ihre Arbeit die Werkzeuge und Programmbibliotheken von EFEU nutzen wollen. Bei EFEU1 Erich Frühstück Entwicklungs-Umgebung handelt es sich um eine Entwicklungsumgebung zur Bildung von Programmbibliotheken und Anwenderprogrammen sowie eine Reihe von Hilfswerkzeugen für die tägliche Arbeit. Besondere Eigenschaften: • Umfangreiche C-Programmbibliotheken; • Makefilegenerierung mit C-Preprozessor aus einem Imakefile oder direkt aus dem Sourcebaum; • Mathematische Funktionen zum Arbeiten mit Datenwürfeln (beliebigdimensional), Polynomen und Zeitreihen; • Befehlsinterpreter mit C++-ähnlicher Syntax; • Generierung von Sourcecode aus Schablonendateien; • Handbuchgenerierung aus Sourcefiles; • Dokumentsprache mit einfacher Syntax, komplexen Einbindemöglichkeiten und verschiedenen Ausgabeformaten (LATEX, HTML, roff, . . . ). Die EFEU-Implementierung bei Synthesis enthält zusätzlich noch eine Reihe von Programmbibliotheken und Kommandos zur Auswertung und Verwaltung von administrativen Daten und für Modellrechnungen. Diese zum Teil sehr umfangreichen Module werden in eigenen Handbüchern behandelt. In der ursprünglichen Form sollte dieses Handbuch den Schwerpunkt C-Programmierung haben, nach Rücksprache mit Mitarbeitern des Instiutes habe ich den EFEU-Interpreter in den Vordergrund gestellt. Trotzdem werden weiterhin einige Ausführungen zur C-Programmierung ins Handbuch einfließen. Zum einen weist der EFEU-Interpreter eine starke Affinität zu C/C++ auf, zum anderen ergeben sich dadurch tiefere Einblicke in die zugrundeliegenden Programmbibliotheken. Erich Frühstück Wördern, Mai 2001 1 1 Inhaltsverzeichnis 1 Einleitung 4 1.1 Entwicklung des EFEU-Interpreters . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2 Verwendung als Tischrechner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2.1 Editieren der Befehlszeilen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.3.1 7 1.3 Ausführbarkeit von Skripts . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Programmkonfiguration 8 2.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2 Konfigurierbare Version von Hello World . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.1 EFEU-Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.2 Esh-Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3.1 Konfigurationsparameter laden . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3.2 Befehle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3.3 Zusatzinformationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Konfigurationsdatei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.4.1 16 2.3 2.4 Standardoptionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Grundkurs esh 3.1 3.2 19 Syntaktische Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.1.1 Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.1.2 Präprozessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1.3 Systemaufrufe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2.1 Ganzzahlwerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2.2 Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.3 Gleitkommawerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2 INHALTSVERZEICHNIS 3.3 3 Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.3.1 Namen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4 Funktionen und Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.5 Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.5.1 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.5.2 Switch-Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Programmumgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.6.1 Programmargumente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.6.2 Umgebungsvariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.6 Kommandos efeuscript – Installation von Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Impressum 27 28 31 Kapitel 1 Einleitung 1.1 Entwicklung des EFEU-Interpreters Die EFEU-Shell esh steht nicht nur für das Kommando gleichen Namens, sondern auch für einen Interpreter, der über C-Bibliotheksfunktion aufgerufen wird. Er nimmt nicht nur eine zentrale Stellung im Rahmen von EFEU ein, sondern ist insbesonders ein wichtiges Werkzeug für die Datenanalysen von Synthesis. Mein erster Versuch einen Befehlsinterpreter zu schreiben reicht weit zurück. Anfang der 80-er Jahre habe ich – weniger aus Notwendigkeit als aus Lust am Programmieren – am Rechner des physikalischen Institutes in Wien meine erste Version eines Befehlsinterpreters geschrieben. Damals kannte ich UNIX und C noch nicht und ich verwendete Fortran. Von dieser Version sind, außer Erinnerungen, keine Spuren zurückgeblieben. Anfang der 90-er Jahre arbeitete ich an der automatischen Generierung von Berichten aus Datenmatrizen. In den dafür verwendeten Skripten zur Datenaufbereitung reichten einfache Datenabfragen nicht mehr aus, es waren auch Berechnungen notwendig. Ich brauchte also schnell einen Befehlsinterpreter. Dieser war, motiviert von Erfahrungen mit PostScript, zunächst stackorientiert. Ein stackorientierter Interpreter läßt sich schnell implementieren. Seine Verwendung ist allerdings nicht benutzerfreundlich. Die Auflösung von Termen und die richtige Abfolge der Funktionsaufrufe verbleibt beim Anwender. Auch muß man sich immer wieder Gedanken machen, in welcher Reihenfolge die Daten am Stack liegen, wie sie der nächste Funktionsaufruf benötigt und welche Umordnungen notwendig sind. Daher begann ich wieder an einem Befehlsinterpreter zu arbeiten. Da ich bereits langjährige Erfahrung in C hatte und diese Sprache lieben gelernt habe, wollte ich einen C-ähnlichen Interpreter schreiben. Primär sollte er von C-Programmen zur Auswertung von Konfigurationsskripts eingesetzt werden. Daher wollte ich, dass vom Interpreter direkt auf C-Datenstrukturen zugegriffen werden kann und einfache Schnittstellen zu C-Bibliotheksfunktionen bestehen. Im Jahr 1994 war der EFEU-Interpreter einsetzbar, und es stand auch das esh-Kommando zur Verfügung. Seine erste wichtige Anwendung war die Umstellung des Wohnungsmarktmodelles auf esh. Dabei kommt eine Version des Interpreters mit zusätzlichen Funktionen speziell für das Wohnungsmarktmodell zum Einsatz. Hier erwies sich auch die Nähe zu C als sehr nützlich. Zeitaufwendige Algorithmen konnten schrittweise durch C-Funktionen ersetzt werden, um die Laufzeit des Wohnungsmarktmodelles zu verbessern. In den nächsten Jahren wurde der Interpreter zunehmend um Funktionen erweitert. So wurde eine Reihe von Hilfsprogrammen zur Manipulation von Datenmatrizen in esh-Skripts umgewandelt. 4 KAPITEL 1. EINLEITUNG 5 Treibende Kraft dafür war einerseits das Wohnungsmarktmodell und andererseits die Berichtsgenerierung. Mit der Entwicklung von texmerge im Jahr 1996 wurde der weiterhin noch verwendete stackorientierte Interpreter endgültig durch esh ersetzt. Ich habe mich mit C++ und objektorientierter Programmierung auseinandergesetzt, ohne aber auf C++ umzusteigen1 Die Gründe dafür lagen zunächst in der Verfügbarkeit – C ist im Gegensatz zu C++ Bestandteil eines jeden UNIX-Systems – und im Overhead von C++ gegenüber von C. Später hatte ich bereits soviele objektorientierte Techniken und Hilfsfunktionen für C entwickelt, dass ein Umstieg nicht mehr angesagt war. . Der esh-Interpreter hat aber deutlich davon profitiert, er wurde um objektorientierte Sprachelemente erweitert. In seiner heutigen Form stellt er durchaus eine Alternative zu Programmen in C++ dar, wenn Laufzeit (Interpreter!) kein Problem darstellt. 1.2 Verwendung als Tischrechner Die Einsatzmöglichkeiten von esh sind vielfältig, so eignet er sich hervorragend als Tischrechner. Wird esh ohne Argumente aufgerufen, startet er im interaktiven Mode und meldet sich mit dem Prompt esh: “. Im folgenden ist ein typischer Dialog mit esh dargestellt (Zeilen ohne Prompt ” sind Ausgaben von esh): esh: 3*5 15 esh: double x = sqrt(2) 1.41 esh: float_prec = 5 5 esh: x 1.41421 esh: Beendet wird esh durch Eingabe des Dateiendezeichens (^D) am Prompt. Ein esh-Skript kann auch mit der Funktion exit(n) beendet werden. Das Argument n bestimmt den Rückgabewert des Kommandos. Mit der ersten Befehlszeile wird der Ausdruck 3*5 berechnet. Die zweite Befehlszeile definiert die Variable x vom Type double und initialisiert sie mit der Quadratwurzel von 2. In der dritten Zeile wird die interne Variable float prec auf 5 gesetzt. Am Ende wird der Wert der Variablen x abgefragt. Anders als in anderen Skriptsprachen müssen Variablen vor ihrer Verwendung deklariert werden. Die Deklaration kann aber an beliebiger Stelle erfolgen und gleich mit einer Wertzuweisung (wie im obigen Beispiel) verbunden werden. Auch kommen in esh die unterschiedlichsten Datentypen zum Einsatz. Andere Skriptsprachen verwenden oft nur Zeichenketten und Gleitkommazahlen. Eine Befehlszeile wird wahlweise mit einem Zeilenvorschub oder einem Strichpunkt abgeschlossen. Bei einem Strichpunkt wird die Befehlszeile ausgewertet, aber das Resultat nicht ausgegeben. Diese Konzeption habe ich octave2 Dabei handelt es sich um eine Skriptsprache für numerische Berechnungen. Ich habe sie eine Zeit lang zur Berechnung von Regressionen verwendet, bevor ich diese Funktionalität in esh eingebaut habe. 1 2 KAPITEL 1. EINLEITUNG 6 abgeschaut. Falls esh in einer Endlosschleife hängt oder man das Ende einer längeren Berechnung nicht abwarten will, kann das Kommando auch mit einem Interrupt (^C) abgebrochen werden. 1.2.1 Editieren der Befehlszeilen Im interaktiven Modus verwendet esh Readline zur Eingabe von Befehlszeilen. Damit können Befehlszeilen erneut abgerufen und editiert werden. Readline wird auch von der bash und anderen interaktiven Kommandos wie gdb, gnuplot oder octave verwendet. Ich setze die Editiermöglichkeiten der bash als bekannt vor und gehe daher hier nicht weiter darauf ein. Eine ausführliche Referenz findet sich in [2, A.16, Seite 1047]. Analog zur bash können History-Zeilen nicht nur über Editierzeichen, sondern auch über eingebaute Kommandos aktiviert werden. Sie sind im folgenden aufgelistet: !h[istory] [n] listet die letzen n Befehle auf. Die Voreinstellung für n ist 10. !h[istory] n k listet die History-Zeilen n bis k auf. !r [n [k ]] führt die History-Zeilen n bis k erneut aus. Fehlt die Angabe von k wird Zeile n ausgeführt, fehlt auch n, wird die letzte Zeile ausgeführt. !fc [n [k ]] ladet die History-Zeilen n bis k in einem Editor (Standardkonfiguration: vi) und führt sie nach dem Speichern aus. Zusätzlich gibt es noch den eingebauten Befehl !eof, der wie die Eingabe des Dateiendezeichens wirkt. Die Befehle !history, !r, !fc und !eof müssen unmittelbar am Zeilenanfang eingegeben werden und stehen nur im interaktiven Modus zur Verfügung. Die History-Zeilen werden nur dann gesichert, wenn esh mit dem Dateiendezeichen (^D) beendet wird. Bei einer Beendigung mit exit oder einem Abbruch mit Interrupt (^C) werden sie nicht ausgegeben. Eine esh-spezifische Vervollständigung von Befehlszeilen ist derzeit noch nicht implementiert. Aufgabe1-1. Rufen sie esh auf und geben sie einzelne Terme ein. Aufgabe1-2. Wiederholen sie einzelne Befehlszeilen unter Zuhilfenahme der History Funktionen. 1.3 Hello World Viele Bücher zur Programmentwicklung, insbesonders in der UNIX-Welt beginnen mit dem bekanntesten aller Programme, nämlich Hello World“. ” Hier ist der Quellcode der Datei hello1.c, die klassische Variante von Hello World“. ” #include <stdio.h> int main (int argc, char **argv) { printf("Hello World!\n"); return 0; KAPITEL 1. EINLEITUNG 7 } Kompiliert wird das Programm mit dem Befehl cc -o hello1 hello1.c Sehen wir uns nun die Datei hello2.esh an, die Esh-Version von Hello World“. ” printf("Hello World!\n"); Aufgerufen wird das Skript mit esh hello2.esh Achtung: Der Strichpunkt am Ende der Zeile ist notwendig, da sonst der Rückgabewert von printf – die Zahl der ausgegebenen Zeichen – ebenfalls ausgegeben wird. 1.3.1 Ausführbarkeit von Skripts Um ein esh-Skript ausführbar zu machen, benötigt es als erste Zeile einen speziellen Eintrag der Form: #!/bindir /esh der das Betriebssystem darüber informiert, dass das Skript vom Kommando esh interpriert werden soll. Für bindir muss aber der absolute Pfadname des Installationsverzeichnisses von esh angegeben werden. Da aber EFEU an den verschiedensten Stellen im System installiert sein kann, sind solche Skripts auf andere Rechner nicht übertragbar. Daher wird ein Trick angewendet und folgende Zeile eingefügt: #!/usr/bin/env esh Das Kommando env dient eigentlich dazu, ein Kommando in veränderter Umgebung ausführen zu lassen. Diese Funktionalität wird zwar nicht benötigt, aber es erlaubt den Start des esh-Interpreters ohne absolute Pfadangabe. Damit das Skript auch ausführbar ist, muß noch der Dateimodus mit dem Befehl chmod geändert werden: chmod a+x hello2.esh Alternativ dazu können diese Schritte mit dem Hilfsprogramm efeuscript durchgeführt werden. Es dient zur Installation eines Skriptfiles und wird in der Regel in Makefiles verwendet. Eine genaue Beschreibung des Kommandos findet sich im Anhang. Ein Esh-Skript wird mit den Optionen -e -c esh installiert. Falls das Sourcefile den Filezusatz .esh“ enthält, sind keine Optionen notwendig, die Einstellungen werden automatisch an Hand ” des Filenamens ermittelt. Das obige Programmbeispiel kann also folgend installiert werden: efeuscript hello2.esh bindir /hello2 Kapitel 2 Programmkonfiguration 2.1 Allgemeines Die X/Open-Spezifikation definiert eine standardisierte Verwendung von Kommandozeilenoptionen. Daneben bietet sie eine standardisierte Programmierschnittstelle für die Bereitstellung von Kommandozeilenschaltern in C-Programmen: die Funktion getopt. In EFEU wird ein ähnlicher Weg gegangen, jedoch wird die Konfiguration der Programmargumente gleichzeitig mit ihrer Dokumentation verbunden. Weiters werden zusätzlich Umgebungsvariablen und Argumente mit einbezogen. Die Formatierung der Beschreibungstexte erfolgt mit efeudoc. Dokumente können damit in mehreren Formaten (roff, LATEX, HTML, . . . ) generiert werden. Weiters besteht die Möglichkeit, die Aufrufsyntax direkt in eine längere Programmdokumentation einzubauen. In der EFEU-Spezifikation können ähnlich wie in der GNU-Version von getopt Optionen an beliebiger Stelle, also auch nach den Argumenten, angegeben werden. Das Flag POSIXLY CORRECT, das bei der GNU-Version von getopt diese Erweiterung deaktiviert, wird von EFEU nicht unterstützt. Shell-Skripts und einzelne Kommandos, die für ihre Arbeiten keine Funktionen der EFEUBibliotheken benötigen, verwenden weiterhin getopt bzw. getopts. X11-Kommandos setzen auf den X11-Standards zur Kontrolle der Programmargumente auf. Näheres dazu kann in den einschlägigen Handbüchern1 Die Online-Handbücher sollten unter Linux verfügbar sein. Eine gedruckte Version gibt es von O’Reilly unter dem Titel The X Window System“. ” nachgelesen werden. 2.2 Konfigurierbare Version von Hello World Zur Veranschaulichung der Programmkonfiguration wollen wir uns nun der konfigurierbaren Version des Programmbeispiels Hello World“ widmen. Diese erlaubt die Bestimmung von Ausgabe” format und Ausgabetext über Kommandozeilenparameter. Der Ausgabetext wird dabei durch das erste (optionale) Argument festgelegt, während das Ausgabeformat über die folgenden zwei Optionen gesteuert wird: Der Ausgabetext wird unter Anführung gestellt. -q 1 8 KAPITEL 2. PROGRAMMKONFIGURATION 9 -f fmt Das Ausgabeformat kann beliebig vorgegeben werden. Hier ist der Quellcode der Datei vhello1.c: #include <stdio.h> #include <stdlib.h> #include <unistd.h> extern int optind; extern char *optarg; int main (int argc, char **argv) { char *fmt = "%s"; char *label = "Hello World!"; int opt; while ((opt = getopt(argc, argv, "qf:")) != -1) { switch (opt) { case ’q’: fmt = "\"%s\""; break; case ’f’: fmt = optarg; break; default: exit(1); } } if (optind < argc) label = argv[optind]; printf(fmt, label); putchar(’\n’); exit(0); } Die Abfrage der Optionen erfolgt mit der Bibliotheksfunktion getopt(3). Ihre genaue Funktionsweise wird hier nicht weiter erläutert. Sie kann dem entsprechenden Handbuch entnommen werden, oder bei [1, Kapitel 4] nachgelesen werden. 2.2.1 EFEU-Version Nun wollen wir uns die Implementation von Hello World“ unter EFEU anschauen. Hier gibt es ” eine Trennung zwischen Funktionalität eines Kommandos und seiner Oberfläche. Die Aufrufsyntax wird in einer eigenen Konfigurationsdatei festgelegt, die erst beim Start des Kommandos geladen wird. Das nächste Kapitel wird sich ausführlich mit der Konfiguration beschäftigen. Sehen wir uns zuerst den Quellcode der Datei vhello2.c an: #include <EFEU/Resource.h> int main (int argc, char **argv) KAPITEL 2. PROGRAMMKONFIGURATION 10 { char *fmt, *label; SetVersion("vhello2.c 1.0"); ParseCommand(&argc, argv); fmt = GetResource("Format", "%s"); label = GetResource("Label", "Hello World!"); printf(fmt, label); putchar(’\n’); exit(EXIT_SUCCESS); } Die Funktion ParseCommand lädt die oben genannte Konfigurationsdatei, analysiert die Kommandoargumente und setzt Resourcen“ in einer internen Struktur. Der Argumentvektor argv wird ” dabei umgeschrieben, nur argv [0] wird dabei nicht verändert. Die Abfrage der Resourcen“ erfolgt mit der Funktion GetResource, wobei das erste Argument ” den Namen der Resource angibt und das zweite Argument den Vorgabewert bestimmt, falls die Resource nicht definiert wurde. In EFEU-Programmen wird exit grundsätzlich nur mit einem der beiden (ANSI C) Makros EXIT SUCCESS bzw. EXIT FAILURE aufgerufen. Diese sind in der automatisch immer eingebundenen Headerdatei <stdlib.h> definiert. Kompiliert wird das Programm mit dem Befehl efeucc -o vhello2 vhello2.c -lefm Das Kommando efeucc implementiert cc und sorgt dafür, das die Suchpfade für Headerdateien und Programmbibliotheken entsprechend von EFEU erweitert werden. Abgesehen von der Angabe der Programmbibliothek efm und dem anderen Kompilernamen gibt es hier keine Überraschungen. Ohne Konfigurationsdatei – diese haben den gleichen Basisnamen wie das Kommando und den Zusatz .cnf“ – verhält sich das Kommando wie hello1. Im folgenden ist nun die Konfigurationsdatei ” vhello2.cnf abgebildet, die nun für die gleiche Syntax wie vhello1 sorgt: # Programmbeispiel: Hello World # $Copyright (C) 2001 Erich Frühstück Format = "%s" Formatierungsanweisung für den Ausgabetext. Label = "Hallo Welt!" Ausgabetext. q|-quote|Format = "\"%s\"" Gibt den Ausgabetext unter Anführung aus. f:fmt|-format:fmt|Format Setzt das Ausgabeformat, die Vorgabe ist |{Format}|. ::arg|Label Bestimmt den Ausgabetext, die Vorgabe ist <"{Label}">. Das erste Kommentar in der Konfigurationsdatei wird gesondert behandelt. Alle Zeilen bis zur ersten Leerzeile bestimmen die Resource Ident (Programmtitel). Beginnt eine der folgenden Zeilen mit der Kennung $C oder $c, bestimmt der Rest der Zeile (Inklusive dem C oder c aber ohne $) die Resource Copyright. KAPITEL 2. PROGRAMMKONFIGURATION 11 Damit können diese beiden Resourcen über das Kommentar gesetzt werden. Der Titel kann auch mehrsprachig spezifiziert werden. Details dazu koönnen unter langfilter(3) nachgeschlagen werdenDie genaue Syntaxbeschreibung der Konfigurationsdatei folgt im nächsten Kapitel. Die Vorteile der EFEU-Version werden sichtbar, wenn eines der folgenden Kommandozeilen ausgeführt wird: vhello2 -? gibt einen Überblick über die zulässigen Optionen und Argumente; vhello2 --help generiert einen Handbucheintrag für das Kommando; vhello2 --help=lp schickt den Handbucheintrag zum Drucker; vhello2 --version liefert die Versionsinformationen zum Kommando; eis -p vhello2 erlaubt eine Abfrage der Kommandoparameter mit eis. 2.2.2 Esh-Version Zum Abschluß wird hier die Esh-Version vom konfigurierbaren Hello World“ vorgestellt. ” #!/usr/bin/env esh /* Programmbeispiel: Hello World! $Copyright (C) 2002 Erich Frühstück */ pconfig ! Version = "vhello3.esh 1.0" Format = "%s" Formatierungsanweisung für den Ausgabetext. Label = "Hallo Welt!" Ausgabetext. q|-quote|Format = "\"%s\"" Gibt den Ausgabetext unter Anführung aus. f:fmt|-format:fmt|Format Setzt das Ausgabeformat, die Vorgabe ist |{Format}|. ::arg|Label Bestimmt den Ausgabetext, die Vorgabe ist <"{Label}">. ! str fmt = getres("Format", "%s"); str label = getres("Label", "Hello World!"); printf(fmt, label); putc(’\n’); Die Konfiguration des Kommandos ist mit der speziellen Kontrollstruktur KAPITEL 2. PROGRAMMKONFIGURATION 12 pconfig ! Konfigurationszeilen ! im Skript integriert. Diese ruft nach der Interpretation der Konfigurationszzeilen automatisch die Funktion ParseCommand auf. Die nachfolgenden Befehlszeilen unterscheiden sich kaum von der C-Verson vhello2.c. Im Verhalten des Kommandos gibt es keinen Unterschied zu vhello2. Auch hier wird das erste Kommemtar im Skript zur Bestimmung von Ident und Copyright herangezogen. 2.3 Konzept Alle konfigurierbaren Parameter eines Kommandos werden in einer Resourcetabelle abgelegt. Jeder Eintrag in diese Tabelle besteht aus einem Namen, einem Wert und einem Beschreibungstext. Die einzelnen Komponenten sind Zeichenketten. Für einen Namen sind grundsätzlich alle Zeichen zugelassen, jedoch empfiehlt es sich, nur Buchstaben, Zahlen, Unterstreichungszeichen und Punkte zu verwenden. Namen, die mit einem Punkt beginnen, sind für eine interne Verwendungen reserviert und sollten nicht für einen Kommandoparameter verwendet werden. Die einzelnen Resourcen werden im Programm mit der Funktion GetResource abgefragt. Dabei wird der Resourcename und ein Vorgabewert angegeben. Dieser wird verwendet, wenn die Resource nicht definiert ist oder einen Nullpointer als Wert zugewiesen hat. Die Zuweisung von Werten zu den einzelnen Resourcen erfolgt bei der Analyse der Programmumgebung (Umgebungsvariablen) und der Befehlszeilenparameter (Optionen und Argumente). Gesteuert wird diese Zuweisung über Kommandodefinitionen. Diese setzen sich aus Kennungen (bestimmen die Aktivierung) und Befehlen (Verändern die Resourcen) zusammen. Die Kommandodefinitionen werden aus speziellen Konfigurationsdateien geladen. 2.3.1 Konfigurationsparameter laden Das Laden der Konfigurationsparameter und die Analyse der Umgebungsvariablen, Optionen und Argumente wird von der Funktion ParseCommand durchgeführt. Als Parameter wird ein Pointer auf die Zahl der Argumente und der Argumentvektor selbst übergeben. Bei der Abfrage der Parameter wird der Argumentvektor umgeschrieben. Im Regelfall besteht er nur mehr aus dem Aufrufnamen des Kommandos. Die Funktion ParseCommand lädt zwei Konfigurationsdateien: efm.cnf und name.cnf, wobei name der Basisname des aufgerufenen Kommandos ist. Gesucht werden die Konfigurationsdateien in den folgenden Verzeichnissen (TOP verweist auf die Hauptbibliothek der EFEU-Installation): • in der aktuellen Bibliothek; • im Verzeichnisnamen des Aufrufnamens (falls definiert); • in den durch die Umgebungsvariable APLLPATH definierten Verzeichnissen; • im Verzeichnis TOP /lib/efeu/$LANG/config, falls die Umgebungsvariable LANG definiert ist; • im Verzeichnis TOP /lib/efeu/config; • im Verzeichnis $HOME/lib/efeu/config. KAPITEL 2. PROGRAMMKONFIGURATION 13 Zu jeder Kennung gehört ein Parameterwert. Bei Umgebungsvariablen ist es der Wert der Umgebungsvariablen, ansonsten das Argument. Bei Optionen ohne Argument ist es ein Nullpointer. Kennungen können einen Vorgabewert enthalten, der immer dann eingesetzt wird, wenn der Parameterwert ein Nullpointer ist. Bestehen Definitionszeilen nur aus Befehlen, werden diese sofort beim Laden ausgewertet und nicht gespeichert. Als Parameterwert wird ein Nullpointer verwendet. Meist werden mit solchen Definitionszeilen Resourcen initialisiert. Optionen sind Kommandozeilenargumente, die mit einem Minus - gekennzeichnet sind. In der X/Open-Spezifikation handelt es sich um Einzeichenoptionen, EFEU erlaubt ähnlich wie der GNUStandard auch lange Optionskennungen. Lange Optionskennungen müssen nicht vollständig angegeben werden, ein signifikanter Teil der Kennung genügt. Lange Optionen beginnen üblicherweise mit einem zusätzlichen Minus, bei EFEU handelt es sich hier aber im Gegensatz zu GNU nur um eine Konvention. Optionen können zwingende oder optionale Argumente besitzen. Einzeichenoptionen können in einem Optionsstring kombiniert werden. Davon darf aber nur die letzte Option ein optionales oder zwingendes Argument besitzen. Ein optionales Argument muß unmittelbar an die Optionskennung angehängt werden, ein zwingendes Argument kann wahlweise an die Optionskennung angehängt oder das nächste Kommandozeilenargument sein. Bei langen Optionskennungen muß vor einem angehängten Argument das Zuweisungszeichen =“ stehen. ” Argumente sind alle Kommandozeilenargumente, die keine Option oder kein Optionsargument sind. Es besteht die Möglichkeit, einen Teil der Argumente über reguläre Ausdrücke abzutesten. Falls der reguläre Ausdruck ein Teilmuster enthält, wird für den Parameterwert nur der entsprechende Teil des Argumentes verwendet. Reguläre Ausdrücke werden hauptsächlich zur Abfrage von Argumenten der Form name=val“ verwendet. Der entsprechende reguläre Ausdruck ist .*=.*“ ” ” Im EFEU-Standard wird ein Argument, das nur aus einem Minuszeichen -“ besteht, durch ” einen Nullpointer ersetzt. Meist wird ein einzelnes Minus anstelle eines Dateinamens stellvertretend für die Standardeingabe bzw. Standardausgabe verwendet. Beginnt ein Argument mit einem Schrägstrich, wird er entfernt und das darauffolgende Zeichen wird nicht interpretiert. Diese Regeln gelten auch für Argumente von Optionen, aber nicht für Umgebungsvariablen und reguläre Ausdrücke. Weiters erfolgt diese Interpretation nur, wenn das Argument abgefragt und nicht nur geprüft wird. Siehe weiter unten! Die Auswertung der Kommandodefinitionen erfolgt nach den folgenden Richtlinien: • Zuerst werden die Umgebungsvariablen ausgewertet. Die zugehörigen Befehle werden nur ausgeführt, wenn die Umgebungsvariable existiert oder die Kennung einen Vorgabewert verschieden von NULL enthält. Die Abfrage erfolgt in der Reihenfolge der Definition. • Im nächsten Schritt wird die Kommandozeile nach Optionen analysiert. Die Abfrage erfolgt in der Reihenfolge des Auftretens. • Schlußendlich werden die Argumente in der Reihenfolge der Definition abgefragt. Hier gibt es eine Besonderheit: Enthält die zugehörige Kommandodefinition keine Befehle, verbleibt das Argument im Argumentvektor, sein Vorhandensein wird aber geprüft. • Am Ende wird geprüft, ob alle Argumente des Argumentvektors verwendet wurden. Bezieht sich eine Kennung auf mehrere Argumente (regulärer Ausdruck, variable Argumentliste), werden die Befehle für jedes Argument einzeln aufgerufen. Wurde das Kommando mit zu wenigen oder zu vielen Argumenten aufgerufen, wird eine kurze Syntaxbeschreibung ausgegeben und das Kommando abgebrochen. KAPITEL 2. PROGRAMMKONFIGURATION 2.3.2 14 Befehle Die einzelnen Befehle werden durch den Namen der zugehörigen Programmresource, einer Auswertungsfunktion und einem optionalen Funktionsargument definiert. Falls der Befehl nur aus dem Resourcenamen besteht, wird die Resource auf den Parameterwert gesetzt. Eine weitere Spezialform ist die Zuweisung: Hier wird die Resource mit dem Wert des Funktionsarguments belegt, der Parameterwert wird ignoriert. Diese kommen bei der Initialisierung von Resourcen oder bei Flags (Optionen ohne Argumente) zum Einsatz. Folgende Funktionen sind definiert: message fmt gibt eine Meldung am Standardfehlerkanal aus. set fmt setzt den Wert der Resourcevariablen entsprechend der Formatdefinition fmt . insert delim fügt den Parameterwert am Anfang der Resourcedefinition mit Trennzeichen delim ein. append delim hängt den Parameterwert mit Trennzeichen delim ans Ende der Resourcedefinition. config name ladet die Konfigurationsdatei mit Namen name. usage fmt generiert eine Aufrufverwendung entsprechend der Formatanweisung fmt und schreibt sie auf den Standardfehlerkanal. manpage name generiert einen Handbucheintrag und schreibt in nach name. Dabei handelt es sich in der Regel um eine Pipeline mit Formatierungskommandos. info name ruft spezifische Informationseinträge über das Kommando ab. exit val bricht das Kommando mit Exit-Status val ab. Wird meist mit Informationsabfragen über das Kommando kombiniert. break bricht die Optionsabfrage ab. Diese Funktion ist nicht für den Standardgebrauch gedacht, da ParseCommand nicht zwischen Fehler und Abbruch unterscheidet, es gibt aber Low-Level Funktionen, mit denen das möglich ist. Mit Ausnahme von insert, append und exit wird das Argument der Auswertungsfunktion mit CmdPar psub überarbeitet. Dabei werden Parameterwerte und Recourcewerte im Argument eingefügt. Falls das Funktionsargument NULL ist, wird es durch den Parameterwert ersetzt. Ein Dollar $ leitet eine Parametersubstitution im Sinne von parsub(3) ein. Die möglichen Formate sind konfigurationsabhängig. Ein Ausdruck Substitutionsdefinitionen der Form {name} wird durch den Resourcewert von name ersetzt. Ein leerer Ausdruck {} wird durch den Parameterwert der Kennung ersetzt. Eine Resourceabfrage kann beliebige Substiutionen enthalten, eine Parametersubstitution kann aber nicht geschachtel werden. Bei der Ersetzung werden Anführungen und Attribute kontrolliert und einzelne Zeichen bei Bedarf geeignet maskiert. Vergleiche dazu CmdPar psub(3). KAPITEL 2. PROGRAMMKONFIGURATION 2.3.3 15 Zusatzinformationen Sämtliche Konfigurationsparameter werden in einer Struktur vom Type CmdPar gespeichert. Die Funktionen ParseCommand und GetResource arbeiten nur mit einer global definierten Struktur dieses Types. Es besteht aber die Möglichkeit, mehrere solcher Strukturen zu verwalten und auszuwerten. Detailierte Informationen dazu können in den entsprechenden Handbucheinträgen der efmProgrammbibliothek nachgeschlagen werden. Vergleiche dazu CmdPar(3), CmdParCall(3), CmdParDef(3), CmdParEval(3), CmdParKey(3), CmdParVar(3), CmdPar eval(3), CmdPar list(3), CmdPar load(3), CmdPar psub(3), CmdPar usage(3), CmdPar write(3), Resource(3) und CmdPar(7). Die Liste der Auswertungsfunktionen kann erweitert werden. So wird bei der Initialisierung des eshInterpreters die Funktion eval definiert, die das Funktionsargument nach der üblichen Parametersubstitution als esh-Ausdruck auswertet. Die Erweiterung muß vor dem Aufruf von ParseCommand erfolgen. 2.4 Konfigurationsdatei Eine Konfigurationsdatei für Programmparameter ist zeilenweise aufgebaut und setzt sich aus den folgenden Zeilentypen zusammen: • Kommentare: Diese sind mit einem Gittersymbol in der ersten Spalte gekennzeichnet. • Beschreibungstexte: Diese sind durch einen Tabulator in der ersten Spalte gekennzeichnet. Aufeinanderfolgende Beschreibungstexte gehören zusammen. Beschreibungstexte nach einem Kommentar oder einer Leerzeile werden ignoriert. • Definitionszeilen: Das sind alle anderen Zeilen, die nicht mit einem Gittersymbol oder einem Tabulator beginnen. Eine Definitionszeile besteht aus einer Liste von Kennungen, gefolgt von einer Liste von Befehlen. Die einzelnen Kennungen werden von einem Pipesymbol | begrenzt, die Befehle durch einen Strichpunkt ; oder den Zeilenvorschub am Ende einer Zeile. Eine allgemeine Definitionszeile ist damit folgend aufgebaut: . . . [Kennung |] [Kennung |] Befehl [; Befehl ] . . . Aufeinanderfolgende Leerzeichen und Tabulatoren vor und nach einem Trennzeichen werden ignoriert, innerhalb einer Kennung oder eines Befehles werden sie auf ein Leerzeichen reduziert. Folgende Kennungen sind definiert: @name Umgebungsvariable name, name Option -name ohne Argument, name:arg Option -name mit zwingendem Argument, name::arg Option -name mit optionalem Argument, :arg notwendiges Argument, KAPITEL 2. PROGRAMMKONFIGURATION 16 ::arg optionales Argument, $arg letztes Argument, *arg variable Argumentliste mit beliebig vielen Argumenten, +arg variable Argumentliste mit mindestens einem Argument, /regex /arg Argument, das dem regulären Ausdruck regex entspricht. Der Parameter arg wird für die Darstellung der Kommandosyntax verwendet. Er wird standardmäßig in kursiv gesetzt, bei regulären Ausdrücken wird Schreibmaschinenschrift verwendet. Jede Kennung kann einen Vorgabewert enthalten. Dieser wird in eckigen Klammern gesetzt und am Ende der Kennung angegeben. Der Vorgabewert kommt zum Einsatz, wenn bei einer Option mit optionalem Argument dieses nicht angegeben wurde, oder wenn anstelle eines Arguments ein einzelnes Minuszeichen steht. Optionen, die aus mehr als einem Zeichen bestehen, sollten mit einem Minuszeichen -“ beginnen. ” Damit werden einerseits Konflikte mit Einzeichenoptionen vermieden und die Kommandosyntax ist GNU-kompatibel. Befehlskennungen sind folgend aufgebaut: name Der Befehl besteht nur aus dem Resourcenamen name. [name] = [val ] Bei dem Befehl handelt es sich um eine Zuweisung. [name] : func [arg] Hier handelt es sich um eine allgemeine Befehlskennung. Der Resourcename name ist optional, aber das Trennzeichen :“ muß zwingend vorhanden sein. Das Funktionsargument arg ist ” durch Leerzeichen oder Tabulatoren vom Funktionsnamen func zu trennen. Beachte den Unterschied zwischen der Zuweisung name = val“ und der Befehlskennung name: ” ” set val“. Im ersten Fall erhält name den Wert von val ohne, im zweiten Fall mit Substitution von Parameterwerten. Der Beschreibungstext nach einer Definitionszeile dient zur Erläuterung der einzelnen Befehlszeilen und wird zur Konstruktion des Handbucheintrages verwendet. Folgt ein Beschreibungstext einer Definitionszeile ohne einer Kennung, dient er zur Beschreibung der Resourcevariablen des letzten Befehls. Obwohl der Vorgabewert einer Kennung am Ende stehen sollte, wird er an jeder beliebigen Position akzeptiert. Die Interpretation der Leerzeichen oder der speziellen Trennzeichen kann durch Setzen von Anführungszeichen (einfach oder doppelt) oder mit dem Backslash als Fluchtsymbol unterbunden werden. 2.4.1 Standardoptionen Eine Reihe von Optionen ist bereits vordefiniert. Im folgenden wird die Datei efm.cnf aufgelistet, die von jedem Kommando automatisch geladen wird: KAPITEL 2. PROGRAMMKONFIGURATION # :*:common options of EFEU-commands # :de:Basisoptionen für EFEU-Kommandos # # # # # # # # # # # # # # # # # Copyright (C) 2001 Erich Frühstück This file is part of EFEU. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.Library. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # :*:predefined resources # :de:Vordefinierte Resourcen Ident = ":*:command:de:Kommando:_: $!" :*:short description of command :de:Kurzbeschreibung des Kommandos Version :*:command version :de:Versions/Revisionsnummer des Kommandos Copyright :*:copyright information :de:Copyright-Informationen Debug = note :*:debug level for messages :de:Debug-Level für Meldungsausgabe GZIP = "-n1" :*:compression mode for gzip :de:Komprimierungsgrad für gzip # :*:dummy entries for direct evaluated environments # :de:Dummy-Einträge für direkt abgefragte Umgebungsvariablen @APPLPATH| :*:path for configuration files. :de:definiert zusätzliche Verzeichnisse für Konfigurationsdateien. @LANG| :*:locale information :de:bestimmt die Sprache für Meldungen und Hilfetexte. :config help # :*:info interface # :de:Info - Schnittstelle 17 KAPITEL 2. PROGRAMMKONFIGURATION 18 -info::entry|:info; :exit 0 :*:show command information :de:listet verfügbare Informationseinträge des Kommandos auf. # :*:debug-level # :de:Debug-Modus -debug::mode[.debug]|Debug :*:set debug level for command See \mref{LogConfig(3)} for details. :de:setzt den Protokollmodus für das Kommando. Vergleiche dazu \mref{LogConfig(3)}. -verbose|Debug = .info :*:set debug level to |.info|. :de:setzt den Debug-Level auf |.info|. Aufgabe2-1. Schreiben sie ein Skript, dass möglichst alle Options- und Argumentdefinitionen verwendet und die entsprechenden Parameter ausgibt. Kombinieren sie mehrere Optionskennungen (z.B: Einbuchstabenkennung und langer Optionsname) zur Setzung eines Parameters. Setzen sie mehrere Parameter mit einer Optionsdefinition. Kapitel 3 Grundkurs esh 3.1 Syntaktische Grundlagen Die Eingabezeilen werden durch einen Präprozessor gefiltert und können Kommentare enthalten. Die Direktiven (Präprozessorbefehle) werden mit einem Gittersymbol #“ eingeleitet und enden ” beim nächsten Zeilenvorschub. Kommentare können wie in C++ Programmen wahlweise durch /* . . . */ oder // . . . Zeilenvorschub gekapselt werden. 3.1.1 Ausdrücke Ausdrücke beginnen entweder mit einem Schlüsselwort oder stellen einen Block oder Term dar. Die Schlüsselwörter generieren Kontrollstrukturen oder spezielle syntaktische Konstruktionen. Sie werden später noch detailiert dargestellt. Ein Block wird mit geschwungenen Klammern { . . . } abgegrenzt und besteht aus einer Liste von Ausdrücken. Blöcke kommen in der Regel bei Funktionsrumpfen, Schleifen und Testblöcken zum Einsatz. Ein Ausdruck, der nicht mit einer geschwungenen Klammer beginnt, wird als Term gelesen. Terme sind Kombinationen von Konstanten, Variablen und Funktionsaufrufen mit Operatoren, z.B: 3 * 3 * x = int a < 5 (x + 5) sqrt(7) x = 13 5 || b > 10 Ein Term wird entweder mit einem Strichpunkt oder einem Zeilenvorschub aufgerufen. Wird ein Term der äußersten Ebene mit einem Zeilenvorschub abgeschlossen, wird das Ergebnis des Terms ausgegeben. Ergebnisse von Termen, die mit einem Strichpunkt abgeschlossen werden oder innerhalb eines Blocks stehen, werden nicht ausgewertet. Terme der äußersten Ebene werden unmittelbar nach dem Lesen ausgewertet. Blöcke werden erst ausgeführt, nachdem sie vollständig gelesen wurden. Einzige Ausnahme: Terme, denen das Schlüsselwort const oder static vorangestellt wird, werden immer sofort nach dem Lesen ausgeführt. Die Verwendung von static wird bei der Beschreibung der Variablen 3.3.1 auf Seite 22 erklärt. 19 KAPITEL 3. GRUNDKURS ESH 20 Die automatische Ausgabe von Berechnungsergebnissen dient primär der interaktiven Verarbeitung. In Skripts empfiehlt es sich, jeden Term mit einem Strichpunkt abzuschließen und Ausgaben über Ausgabefunktionen zu steuern. 3.1.2 Präprozessor Der verwendete Präprozessor hat eine ähnliche Syntax wie der C-Präprozessor. Es gibt jedoch einen wesentlichen Unterschied: Der Präprozessor wird nicht zur Vorverarbeitung der gesamten Datei verwendet, sondern ist zeilenweise implementiert und arbeitet direkt mit dem Befehlsinterpreter zusammen. Insbesonders kann durch verändern von Variablen im Befehlsmodus auch die Verarbeitung nachfolgender Präprozessorzeilen beeinflußt werden. Eine Direktive, bei der nach dem Startzeichen #“ ein Sonderzeichen folgt, wird ebenfalls als ” Kommentarzeile betrachtet. Insbesonders gilt das auch für die Zeile: #!/efeu/bin/env esh Durch Einfügen dieser Zeile zu Beginn der Datei kann ein Skript ausführbar gemacht werden. Diese Eigenschaft des Präprozessors sollte ausschließlich für den eben dargestellten Zweck und nicht für allgemeine Kommentare verwendet werden. Die folgenden Präprozessordirektiven können verwendet werden: #include <file> Einbinden der Datei file. Die Suche erfolgt in den durch die Variable IncPath definierten Verzeichnissen. #include "file" Wie oben, jedoch zusätzliche Suche im aktuellen Verzeichnis, falls dieses nicht im Suchpfad enthalten ist. #if expr Falls der Ausdruck expr logisch wahr liefert, werden die nachfolgenden Zeilen interpretiert, ansonsten übersprungen. #elif expr Falls kein vorangegangener Ausdruck einer #if oder #elif Anweisung logisch wahr war und expr einen logisch wahren Ausdruck liefert, werden die nachfolgenden Statements ausgeführt. #else Falls kein vorangegangener Ausdruck einer #if oder #elif Anweisung logisch wahr war, werden die nachfolgenden Statements ausgeführt. #endif Ende eines Abfrageblockes. #define name repl Definiert einen Makro name der durch repl ersetzt wird. #define name(arglist ) repl Definiert einen Makro name mit Argumenten. Die öffnende Klammer muß unmittelbar nach dem Namen folgen. #undef name Löscht die Definition des Makros name #ifdef name Testet, ob der angegebene Makro definiert ist. KAPITEL 3. GRUNDKURS ESH 21 #ifndef name Testet, ob der angegebene Makro nicht definiert ist. #error Text Liefert eine Fehlermeldung. 3.1.3 Systemaufrufe Systemaufrufe können direkt in EFEU-Skripts eingebaut werden. Jede Zeile, die mit einem ! beginnt, wird als Systemaufruf interpretiert. Ausnahmen davon sind nur readline-Kommandos im interaktiven Modus. In die Befehlszeile können Parameter mit der üblichen Parametersubstitution eingebaut werden (Vergleiche dazu parsub). Alternativ dazu steht die Funktion system() zur Verfügung. 3.2 Konstanten 3.2.1 Ganzzahlwerte Wie in C gibt es auch in esh eine Reihe von Ganzzahldatentypen. Es gibt jedoch einige Unterschiede: bool ist ein Ganzzahltyp für logische Ergebnisse mit den Ausprägungen 0 (false) und 1 (true). Sie sind über den C-Datentyp int implementiert. byte ist ein Ganzzahltyp mit einem Wertebereich von −128 bis 127. Er entspricht dem C-Datentyp signed char. short entspricht dem C-Datentyp short. int entspricht dem C-Datentyp int. long entspricht dem C-Datentyp long int. unsigned entspricht dem C-Datentyp unsigned int. size t entspricht dem C-Datentyp long unsigned int, der in der Regel mit dem C-Datentyp size t kompatibel sein sollte, aber nicht immer so definiert ist. In Esh sind long und unsigned Datentypen, während es sich in C nur um Typmodifikatoren handelt. Die Bytelängen der Ganzzahldatentyp sind architekturabhängig, sie entsprechen aber immer dem gleichwertigen Datentyp in C. In C wird für boolsche Werte der Datentyp int verwendet. In esh wird int vom Datentyp bool abgeleitet (Vererbung) und kann damit immer auch anstelle eines boolschen Wertes eingesetzt werden. Ganzzahlkonstanten können wahlweise Dezimal, Oktal oder Hexadezimal angegeben werden. Eine Hexadezimalzahl beginnt mit 0x“, gefolgt von den Hexadezimalziffern (0−9, a-f). Die Buchstaben ” können wahlweise groß oder klein geschrieben werden. Eine Oktalzahl wird von einer Dezimalzahl KAPITEL 3. GRUNDKURS ESH 22 durch eine führende 0 unterschieden. Sollte aber eine der Ziffern 8 oder 9 vorkommen, wird die Konstante als Dezimalzahl interpretiert. Ein nachgestelltes l“ symbolisiert einen langen Datenwert, ein nachgestelltes u“ einen vorzei” ” chenfreien Datenwert. Diese beiden Flags können beliebig kombiniert werden und wahlweise groß oder klein geschrieben werden. Boolsche Konstanten werden durch die Schlüsselwörter true und false dargestellt. Es gibt keine Ganzzahlkonstanten vom Typ byte oder short. 3.2.2 Zeichenketten Ein einzelnes Zeichen wird durch einfache Anführungszeichen definiert, eine Zeichenkette (String) durch doppelte Anführungszeichen. Eine Zeichendefinition darf mehrere Zeichen enthalten, es wird aber nur das erste Zeichen verwendet. Für sehr lange Zeichenketten gibt es eine alternative Konstruktion über das Schlüsselwort string in Kombination mit ! als Begrenzungsmarke. Teil des Ausdrucks vor der Zeichenkette string ! Zeichenkette ! Rest des Ausdrucks nach der Zeichenkette Zwischen string und ! können beliebige Leerzeichen oder Tabulatoren stehen, nach ! muss ein Zeilenvorschub folgen. Die Zeichenkette endet beim ersten ! unmittelbar am Zeilenbeginn. Innerhalb der Zeichenkettendefinition werden Zeichen normal interpretiert, Präprozessordirektiven (z.B. #include) und Kommentare werden interpretiert. Eine so definierte Zeichenkette ist immer mit einem Zeilenvorschub abgeschlossen. 3.2.3 Gleitkommawerte double normale Gleitkommawerte. float kurze Gleitkommawerte. Gleitkommakonstanten sind immer vom Typ double. Sie werden von Ganzzahlkonstanten durch die Anwesenheit eines Dezimalpunktes oder eines Exponenten unterschieden. Alle Rechenoperationen werden mit double ausgeführt. Bei großen Vektoren kann damit Speicherplatz auf Kosten der Genauigkeit gespart werden. 3.3 3.3.1 Variablen Namen Ein Name besteht aus einer Folge von Buchstaben und Ziffern. Das erste Zeichen muß ein Buchstabe sein. Ein Unterstreichungszeichen “ gilt als Buchstabe. ” Mit Hilfe des Schlüsselwortes operator können auch beliebige Namen angesprochen werden. Der Name wird dabei entweder unter doppelte Anführung gesetzt oder durch Leerzeichen bzw. Tabulatoren begrenzt. Nach dem Schlüsselwort dürfen Leerzeichen und Tabulatoren stehen. Obwohl diese Konstruktion allgemein verwendet werden kann, sollte sie nur zur Definition von Operatorfunktionen (Funktionen, deren Aufruf durch einen Operator bewirkt wird) verwendet werden. KAPITEL 3. GRUNDKURS ESH 23 Variablendefinitionen, denen das Schlüsselwort static vorangestellt ist, werden sofort beim Parsen der Befehlszeilen eingerichtet. Innerhalb eines Blocks wird für statische Variablen eine andere Variablentabelle als für dynamische Variablen (solche die erst bei der Abarbeitung des Blocks angelegt werden) verwendet. Die innerhalb eines Blocks definierten Variablen (statisch oder dynamisch) sind außerhalb des Blocks nicht sichtbar. Steht vor einer Variablendefinition das Schlüsselwort const, wird damit eine Konstante eingerichtet. Der mit der Zuweisung festgelegte Wert kann nicht mehr verändert werden. 3.4 Funktionen und Operatoren Eine Funktionsdefinition hat die allgemeine Form type name ( arglist ) expr Normalerweise ist expr ein Block von Ausdrücken, in $! kann aber auch ein einzelner (nicht leerer) Ausdruck stehen. Falls die Funktion keinen Wert zurückliefern soll, ist als Datentyp void zu verwenden. Die folgenden Funktionsdefinitionen sind gleichwertig: int f (int x) x + 1; int f (int x) return x + 1; inline int f (int x) { return x + 1; } Im EFEU-Befehlsinterpreter hat das Schlüsselwort inline primär etwas mit Sichtbarkeit zu tun. Eine inline Funktion sieht alle Variablentabellen, die auch in der Zeile mit dem Funktionsaufruf sichtbar waren. Alle Funktionen, die nur aus einem einzelnen Ausdruck bestehen, gelten als inline Funktionen. Wie in C++ können Funktionsargumente Vorgabewerte besitzen. Diese müssen dann beim Aufruf nicht angegeben werden. Die allgemeine Form eines Funktionsarguments ist: type [ & ] name [ = value ] Das & zeigt an, dass das Argument ein gültiger L-Wert sein muss. Eine Tilde ... anstelle des Funktionsarguments steht für eine variable Argumentliste. Auf sie kann innerhalb der Funktion unter dem Namen va list zugegriffen werden. Virtuelle Funktionen Wie in C++ können Funktionen mit verschiedenen Argumentlisten überladen werden. Überladene Funktionen werden mit dem Schlüsselwort virtual deklariert. Der Datentyp solcher Funktionen ist VirFunc. Jede Funktion kann in eine virtuelle Funktion konvertiert werden. Eine virtuelle Funktion kann auch in eine gewöhnliche Funktion umgewandelt werden. Dies gechieht mit einem Prototype-Cast wie im folgendem Beispiel: Func f = operator+ (int a, int b);; Nun kann f zur Addition von zwei Ganzzahlwerten verwendet werden. Beachte die zwei Strichpunkte am Ende der Zuweisung: Der erste gehört zum Prototype (und unterscheidet ihn von einer Funktionsdefinition, der zweite schließt den Ausdruck ab. Typgebundene Funktionen Funktionen können auch an einen Datentyp gebunden werden. Sie haben die allgemeine Form: KAPITEL 3. GRUNDKURS ESH 24 type btype::name [ & ] ( arglist ) expr Falls nach dem Funktionsnamen ein & steht, kann die Funktion nur für L-Werte verwendet werden. Eine gebundene Funktion wird folgend aufgerufen: obj .name(args) Dabei ist obj ein Objekt vom Type btype. Der Datentyp einer typgebundenen Funktion ist ObjFunc. Dabei kann es sich sowohl um eine virtuelle, als auch um eine gewöhnliche Funktion handeln. In gebundenen Funktionen kann mit dem Schlüsselwort this auf das zugehörige Datenobjekt zugegriffen werden. Operatoren Operatoren werden intern wie Funktionen behandelt. Mit dem Schlüsselwort operator kann ein Operatorname direkt angesprochen werden. Folgende Schreibweisen sind zulässig: operator op operator "op" Bei der ersten Schreibweise muß nach op ein Leerzeichen folgen, vor op kann ein Leerzeichen stehen. Damit linke Operatoren von rechten unterscheidbar sind, werden sie intern mit dem Zusatz () versehen (z.B: -() für die Negation. Dies ist bei der Definition von Funktionen zu beachten. Folgende Terme sind gleichwertig: a + b operator+ (a, b) Operatoren sind in der Regel virtuelle Funktionen. Alle Zuweisungsoperatoren sind gebundene, virtuelle Funktionen. Spezielle Funktionen Funktionen, die den gleichen Namen wie ein zuvor definierter Datentyp haben, definieren Konstruktoren und Konverter. Konverter werden meist indirekt bei Zuweisungen, der Wertübergabe bei Funktionsaufrufen oder durch explizite Typumwandlungen (casts) aufgerufen. Konstruktoren haben die Form virtual type type ( arglist ) Die spezielle Form type type () wird Copy-Konstruktor genannt. Ist er definiert, wird er jedesmal beim Kopieren eines Datenelementes aufgerufen. Im Gegensatz dazu ist type type (void) ein gewöhnlicher Konstruktor ohne Argumente. Konverter haben die Form KAPITEL 3. GRUNDKURS ESH 25 tg type src type () mit einer leeren Argumentliste. Die Ausgangsdaten werden unter dem Namen this referiert. Falls der Zieldatentype void ist, definiert die Funktion den Destruktor für den Datentyp, der jedesmal aufgerufen wird, wenn ein Objekt diesen Types gelöscht wird. Copy-Konstruktor und Destruktor können als Spezalfall eines Konverters gesehen werden. Wegen der internen Speicherbereinigung werden sie kaum benötigt. Bei ihrer Definition ist besondere Vorsicht notwendig: Sobald ein Objekt dieses Types kopiert wird (z.B. bei der Weitergabe an eine andere Funktion), führt der Aufruf dieser Funktion zu einer endlosen Rekursion. 3.5 Kontrollstrukturen 3.5.1 Schleifen while (cond ) cmd do cmd while (cond ) Definiert eine Schleife. Der Ausdruck cmd wird solange ausgeführt, wie cond logisch wahr ist. Bei cmd handelt es sich entweder um eine einfache Befehlszeile oder einen Block. Bei der zweiten Form wird cmd zumindest einmal ausgeführt. for (a; cond ; b) cmd Zu Beginn der Verarbeitung wird a ausgeführt. Der Ausdruck cmd wird solange ausgeführt, wie cond wahr ist. Nach jedem Schleifendurchlauf wird b ausgeführt. for (name in list ) cmd Für jedes Element der Liste list wird cmd ausgeführt. Name ist der Name einer temporären Variablen, die das aktuelle Element der Liste enthält. Anstelle von list kann auch ein einzelnes Objekt stehen, das in eine Liste konvertierbar ist (z.B: ein Vektor). Bei Schleifen kann ein Block vorzeitig mit der break Anweisung verlassen werden. Die continue Anweisung startet einen neuen Zyklus. Bedingungen if (cond ) cmd1 if (cond ) cmd1 else cmd2 Falls die Bedingung cond wahr ist, wird cmd1 ausgeführt, ansonsten wird, falls das Schlüsselwort else angegeben ist, cmd2 ausgeführt. 3.5.2 Switch-Anweisung Ein Switch-Statement hat die Syntax: switch (expr ) { label : cmdlist label : KAPITEL 3. GRUNDKURS ESH 26 cmdlist ... } Bei label handelt es sich um einen Label der Form case val oder default. Der Ausdruck val wird bereits beim Lesen des Switch-Statements berechnet. Der Ausdruck expr wird der Reihe nach mit allen Labels verglichen. Stimmt er mit val überein, wird die nachfolgende Liste von Ausdrücken ausgewertet. Falls in der Liste keine break-, continue- oder return-Anweisung vorkommt, werden die Ausdrücke des nachfolgenden Labels mit ausgewertet. Stimmt expr mit keinem Wert val der Labels überein, werden die Ausdrücke von default verwendet. Anders als in C sind in esh beliebige Datentypen für Switch-Statements erlaubt. Die einzige Bedingung ist, daß der Operator == für diesen Datentype definiert ist. So können in esh z.B. Zeichenketten oder Reguläre Ausdrücke in Switch-Statements verwendet werden. Nicht jeder Datentyp eignet sich gut für Switch-Statements. 3.6 Programmumgebung 3.6.1 Programmargumente Wird ein esh-Skript aufgerufen, können zusätzliche Argumente zur Steuerung des Skripts übergeben werden. Dazu stehen die Variablen argc und argv zur Verfügung. Bei argc handelt es sich um einen Ganzzahlwert mit der Zahl der übergebenen Argumente, während argv der Vektor mit den Argumenten ist. Das erste Element argv[0] enthält immer den Aufrufnamen des Skripts, argc ist immer größer oder gleich 1. Die Funktion shift mit dem optionalen Parameter n = 1 löscht n Argumente an der Position 1 aus dem Argumentvektor. Das erste Argument argv[0] bleibt dabei unverändert. Die Zahl der Argumente argc wird entsprechend angepaßt. 3.6.2 Umgebungsvariablen Umgebungsvariablen können mit der Funktion getenv abgefragt werden. Die Syntax ist: str getenv (str name, str def = NULL) Dabei bestimmt name den Namen der Umgebungsvariable und def ist der Vorgabewert, falls die Umgebungsvariable nicht definiert ist. Beispiele: getenv("HOME") getenv("LANG", "us") Das nächste Kapitel beschäftigt sich ausführlicher mit der Abfrage von Programmargumenten. Kommandos 27 efeuscript(1) 28 BEZEICHNUNG efeuscript – Installation von Scripts ÜBERSICHT efeuscript [ --help[=type] ] [ --version ] [ -u ] [ -g ] [ -e ] [ -r ] [ -s /expr/repl/ ] [ -c name ] src tg BESCHREIBUNG Das Kommando ergänzt das Skript src mit einer Interpreterkennung. Dabei wird der vollständige Pfad des Interpreters automatisch ermittelt. Falls bereits eine Interpreterkennung vorhanden ist, wird diese nur geprüft und nicht verändert. Die folgenden Optionen und Argumente werden vom Kommando efeuscript akzeptiert: --help[=type] generiert eine Beschreibung des Kommandos. Der zusätzliche Parameter type bestimmt die Formatierung und die Ausgabe der Beschreibung. term Terminalausgabe (default) raw Rohformat für efeudoc man nroff/troff Sourcen für man lp Ausgabe zum Drucker --version gibt die Versionsnummer des Kommandos aus. -u Nur Eigentümer erhält Ausführungsrechte -g Nur Eigentümer und Gruppe erhalten Ausführungsrechte -e /usr/bin/env zum Start des Interpreters verwenden -r exec zum Start des Interpreters verwenden -s /expr/repl/ Verwende sed zum Ersetzen von expr durch repl . Mehrfachangaben sind möglich. Anstelle von / kann auch ein anderes Trennzeichen verwendet werden. -c name Name des Befehlsinterpreters, Vorgabe cmd src Pfadname des Quellskripts tg Pfadname des Zielskripts COPYRIGHT Copyright (C) 2001 Erich Frühstück Literaturverzeichnis [1] Neil Mathew, Richard Stones. Linux Programmierung, Bonn: MITP-Verlag, 2000 [2] Karsten Günther, Kestler Grelck, Thorsten Zilm. Linux – Die User-Befehlsreferenz, Bonn: MITP-Verlag, 1999 [3] Bjarne Stroustrup. Die C++ Programmiersprache, Addison.Wesely, Bonn 1992 [4] Erich Frühstück. Make und Co, EFEU–Handbuch [5] Erich Frühstück. Die Programmbibliothek efm, EFEU–Handbuch [6] Erich Frühstück. Die Readline-Schnittstelle, EFEU–Handbuch [7] Erich Frühstück. Die Programmbibliothek md, EFEU–Handbuch 29 Index !eof, 6 !fc, 6 !history, 6 !r, 6 #, 19 $HOME/lib/efeu/config, 12 ^C, 6 ^D, 5, 6 Abbruch von esh, 6 APLLPATH, 12 argc, 26 Argumente, 13 Argumentvektor, 12 argv, 26 Auswertungsfunktion, 14 Beenden von esh, 5 Befehlszeilenparameter, 12 Beschreibungstext, 15, 16 CmdPar, 15 CmdPar psub, 14 const, 23 Datenmatrizen, 4 Definitionszeile, 15 Direktiven, 19 EFEU, 1 efeudoc, 8 EFEU-Spezifikation, 8 efm.cnf, 12 esh-Interpreter, 15 exit, 5, 6 Filezusatz .cnf“, 10 ” float prec, 5 Funktionsargument, 14 Kommandodefinitionen, 12 Kommentar, 15 Konfigurationsdatei, 15 LANG, 12 name.cnf, 12 operator, 22 Optionen, 13 Parameterwert, 13 ParseCommand, 12, 14, 15 POSIXLY CORRECT, 8 Präprozessor, 19 Programmdokumentation, 8 regulärer Ausdruck, 16 Resourceabfrage, 12 Resourcetabelle, 12 shift, 26 stackorientierter Interpreter, 4 static, 23 Substitution, 14, 16 Substitutionsdefinition, 14 texmerge, 5 Tischrechner, 5 TOP /lib/efeu/$LANG/config, 12 TOP /lib/efeu/config, 12 Vorgabewert, 16 Wohnungsmarktmodell, 4 X/Open-Spezifikation, 8, 13 Zugriff auf History-Zeilen, 6 Zuweisung, 14 Generierung von Berichten, 4 getenv, 26 getopt, 8 getopts, 8 GetResource, 12 IncPath, 20 30 Impressum Eigentümer und Verleger: Erich Frühstück A-3423 St.Andrä/Wördern Für den Inhalt verantwortlich: Erich Frühstück Herstellung und Redaktion: Erich Frühstück Wördern 2001 31