Bereits in der Grundeinführung am Anfang des Heftes haben wir die ksh als Kommandointerpretierer kennengelernt. Neben der Eigenschaft, Eingaben des Benutzers in Programmaufrufe umzusetzen, kann die Shell auch Programme ausführen, die in einer speziellen Kommandosprache geschrieben wurden. Diese Programme nennt man Shell-Scripts. Shell-Scripts können immer wiederkehrende Arbeitsabläufe automatisieren und vereinfachen. Dieses Kapitel befaßt sich mit der Erstellung von Shell-Scripts und der Verwendung von Shell-Variablen und Umgebungsvariablen.
Im Normalfall werden Daten über die Tastatur ein- und am Bildschirm ausgegeben. Man bezeichnet daher die Tastatur als Standardeingabe und den Bildschirm als Standardausgabe. Die UNIX-Shells bieten jedoch syntaktische Konstruktionen, mit deren Hilfe man die Ein- oder Ausgabe eines Programms in eine Datei oder in ein anderes Programm umleiten kann. Einige UNIX-Programme wurden speziell für die Verwendung im Rahmen dieser Ein- und Ausgabeumlenkung entwickelt. Mit deren Hilfe lassen sich viele auf den ersten Blick komplizierte Aufgaben einfach durch Verketten verschiedener Programme zu einem Befehlsstrom lösen.
Die Multitasking-Fähigkeiten von UNIX sowie das im Vergleich zu DOS wesentlich effektivere Dateisystem sorgen dafür, daß die hier angegebenen Konstruktionen auch in der täglichen Arbeit ein effektives und schnelles Hilfsmittel darstellen. Im Gegensatz zu DOS wird nämlich unter UNIX nicht immer die ganze Ausgabe eines Programms auf dem Datenträger gespeichert, bevor das nächste Programm startet, sondern es wird ständig zwischen den verschiedenen Programmen umgeschaltet und die anfallende Datenmenge dadurch gering gehalten. Auf diese Art müssen die Daten im Regelfall gar nicht auf den Datenträger geschrieben werden, sondern werden über im Hauptspeicher vorhandene Puffer weitergegeben.
In der Grundeinführung haben wir gesehen, daß die Shell gewisse Zeichenfolgen in der Kommandozeile interpretiert und entweder durch andere Zeichenfolgen ersetzt oder auf Grund dieser Zeichenfolgen gewisse Aktionen ausführt (Ein-/Ausgabeumlenkung, Hintergrundprozesse, ... ). Dieses Verhalten der Shell ist manchmal unerwünscht, zum Beispiel, wenn einmal wirklich ein * in der Kommandozeile stehen soll. Man kann daher auf mehrere Arten verhindern, daß die Shell gewisse Ersetzungen durchführt.
Dieses Verhindern von Interpretationen bestimmter Zeichen wird auch als Quoting bezeichnet.
Alle in den nachfolgenden Abschnitten erklärten Quoting-Mechanismen lassen sich gefahrlos mit Hilfe des Befehls echo testen.
Der Backslash (\) macht die Sonderbedeutung des nachfolgenden Zeichens unwirksam.
echo \*
erzeugt zum Beispiel die Ausgabe
*
Eine Besonderheit hierbei ist die Eingabe von \ mit nachfolgendem Drücken der ENTER-Taste. Der dadurch am Bildschirm sichtbare Zeilenvorschub wird bei der Interpretation der Kommandozeile komplett ignoriert. Auf diese Art ist es möglich, lange Befehle in mehreren aufeinanderfolgenden Zeilen einzugeben, um die Übersichtlichkeit zu verbessern. Ruft man die so eingegebenen Zeilen mit Hilfe der History-Funktionen der Unix-Shell noch einmal auf, so werden die Zeilenumbrüche in der Form ^J dargestellt. ^J ist dabei ein einzelnes Zeichen (Ctrl-J), was man auch beim Bewegen des Cursors auf der Zeile feststellen kann.
Will man mehrere Zeichen oder Worte vor einer Interpretation schützen, so ist das Verfahren mit vorangestelltem Backslash etwas unpraktikabel. Für diesen Fall kann man die entsprechende Zeichenkette in zwei Hochkommata einschließen. Die ist insbesondere dann sinnvoll, wenn mehrere durch Zwischenräume getrennte Worte als ein einziges Argument einem Programm übergeben werden sollen. Ohne den Einschluß in Hochkommata würde die Shell in diesem Fall Worttrenner erkennen und dementsprechend mehrere Einzelparameter erzeugen. Innerhalb einer mit Hochkommata versehenen Zeichenkette darf dann allerdings kein weiteres Hochkomma stehen, auch nicht als \'.
Es ist möglich, nicht gequotete und gequotete Zeichenketten zu einem Parameter zusammenzufügen. Dies geschieht einfach durch hintereinanderschreiben der entsprechenden Ausdrücke.
Erklärung:
Der erste echo-Befehl hat keinerlei Quoting und zeigt damit den Inhalt der vorher gesetzten Variablen c an.
Der zweite echo-Befehl hat den Ausdruck $c gequotet und verhindert damit, daß die Shell eine Variablenersetzung vornimmt. Daher wird die Zeichenkette $c unverändert angezeigt.
Der letzte echo Befehl zeigt das
Hintereinanderschreiben von einem nicht gequoteten und einem
gequoteten Ausdruck. Die Variablenersetzung wird einmal ausgeführt
und einmal unterbunden.
Das Anführungszeichen läßt im Gegensatz zum Hochkomma Ersetzungen von Shell-Variablen und die Interpretation des Backquote-Konstruktes zu, verbietet aber die Interpretation von Dateinamen-Kurzschreibweisen (*, ? ... ). Dies macht man sich am besten klar, wenn man das folgende Kommando eingibt:
echo "* `ls`"
Das Ergebnis zeigt nach dem *, der augenscheinlich nicht interpretiert wird, die Liste von Dateinamen, die ls erzeugt.
Innerhalb einer von Anführungszeichen eingeschlossenen Zeichenkette ist des weiteren das Quoting mit Hilfe des Backslash erlaubt. Insbesondere darf auch ein in der Zeichenkette vorkommendes Anführungszeichen durch einen Backslash gequotet werden, so daß die Shell es nicht als Ende der Zeichenkette erkennt.
Hinweis: Durch Hochkomma oder Anführungszeichen begrenzte Strings dürfen sich über mehrere Zeilen erstrecken. Wenn also überraschend ein Prompt > auftritt, dann solltet Ihr überprüfen, ob Ihr irgendwo das schließende Anführungszeichen vergessen habt.
Die Unix-Shell bietet die Möglichkeit, Variablen zu definieren und ihnen bestimmte Werte zuzuweisen. Die Namen der Variablen sind frei wählbar, jedoch sind bestimmte Variablen von der Shell mit einer Sonderbedeutung versehen, daher sollte man für selbstdeklarierte Variablen nur Namen verwenden, die mit einem Buchstaben beginnen.
Um eine Variable zu deklarieren, weist man Ihr einen Wert zu. Beispiel:
versuch='Hallo, Welt'
Die Hochkommata verhindern, daß die Shell an der Zeichenkette irgendwelche Interpretationen vornimmt.
Anschließend kann man die Belegung von versuch mit dem Kommando echo $versuch abfragen. Das vorangestellte $-Zeichen sagt der Shell, daß die folgende Zeichenkette der Name einer Variablen oder einer Umgebungsvariablen ist.
Eine Liste aller derzeit gesetzten Shellvariablen erhält man durch den Befehl set.
Neben den internen Variablen der Shell gibt es die sogenannten Environment-Variablen oder Umgebungsvariablen. Diese Variablen sind im Gegensatz zu den Shell-Variablen auch allen Programmen zugänglich, die von der Shell gestartet werden, und können von diesen abgefragt werden.
Zur Anzeige aller gesetzten Umgebungs-Variablen verwendet man den Befehl env. Mit dem Befehl export erklärt man eine Shell-Variable zur Umgebungsvariable. Nachfolgende Änderungen an der Shellvariable ändern dann automatisch auch die Umgebungsvariable mit.
Hinweis: Eine Umgebungsvariable wird immer nur auf nachfolgend gestartete Programme vererbt. Bereits laufende Programme erfahren keine Änderung, da bei jedem Programmstart für dieses eine Kopie der Umgebung angelegt wird. Änderungen werden auch niemals an den Vaterprozeß weitergegeben, das ist nicht möglich.
Einige wichtige benutzerspezifische Daten werden bereits beim Anmelden vom System belegt. Diese sind zum Teil von der benutzten Shell abhängig. Die wichtigsten Variablen bei der K-Shell sind (Großschreibung beachten):
Auch diese voreingestellten Variablen lassen sich ändern; Änderungen können aber, wenn sie unbedacht vorgenommen werden, recht ärgerliche Wirkungen haben.
Wenn Ihr beispielsweise den PATH ersetzt, so findet Ihr
anschließend viele Kommandos nicht mehr. So könnt Ihr den
Pfad ergänzen:
PATH=$PATH:neuesverz.
Kommandofolgen, die öfter benötigt werden, können in Shellprozeduren zusammengefaßt werden. Im einfachsten Fall sind Shellprozeduren die sequentielle Verkettung von Befehlen, die andernfalls in gleicher Reihenfolge interaktiv vom Benutzer gegeben würden; aber auch Schleifenkonzepte und Unterprogrammstrukturen sind möglich.
Eine Kommandofolge sei in der Datei file gespeichert. Dann kann diese Shell-Prozedur auf drei Arten aufgerufen werden:
sh file ruft eine neue Shell auf, die die Befehle in der Datei file ausführt und sofort danach wieder beendet wird.
Wenn man die Datei ausführbar macht (z. B. mit chmod u+x file), kann sie direkt durch Angabe ihres Namens gestartet werden. Auch in diesem Fall wird intern eine eigene Shell gestartet, die die Befehle in file abarbeitet.
Schließlich gibt es noch die Möglichkeit, eine Prozedur von der Shell selbst, mit der man arbeitet, ausführen zu lassen. Dieses sogenannte Sourcing ist notwendig, wenn die Prozedur Umgebungsvariablen setzen soll, weil diese sonst nur in der die Prozedur ausführenden Subshell gesetzt würden und bei deren Beendigung wieder verloren gingen. Dies bewirkt man, indem man dem Dateinamen den Befehl ., also einen Punkt (und ein Leerzeichen !) als eigenes Wort, voranstellt:
. Datei
Die Dateien .profile und .kshrc werden
ebenfalls gesourced.
Mit Hilfe eines Editors werden die Shellkommandos in eine Datei geschrieben. Nach dem Editieren sind die Prozeduren, falls sie inhaltlich und syntaktisch korrekt sind, ablaufbereit. Wie erwähnt interpretiert die Shell die Kommandos, d. h. einen Compiler o. ä. gibt es nicht.
Kommentare werden in einem Shell-Script durch das Nummernzeichen (#) eingeleitet. Der gesamte Inhalt einer Zeile ab dem Nummernzeichen wird dann von der Shell nicht beachtet. Kommentare können an jeder beliebigen Stelle in einem Shell-Script stehen. Eine Besonderheit bildet jedoch ein Kommentar in der allerersten Zeile der Scriptdatei.
Da es auf dem System unterschiedliche Shells mit zum Teil auch unterschiedlicher Kommandosyntax gibt (insbesondere, was Schleifen oder Abfragen betrifft), kann man in einem Shellscript festlegen, welches Programm zur Interpretation dieses Scripts eingesetzt werden soll. Dazu muß in die erste Zeile des Shellscripts die Zeichenfolge #!, gefolgt vom vollständigen Pfadnamen des gewünschten Programms stehen. Dies bewirkt intern, daß eine Kommandozeile generiert wird, in der das so angegebene Kommando steht, gefolgt vom Dateinamen des Shellscripts. Um zum Beispiel das Skript myprog von der Unix-Shell ausführen zu lassen, muß dessen erste Zeile so lauten:
#!/bin/ksh
Der Aufruf, der dann letztendlich erzeugt wird, wenn man in der Kommandozeile myprog angibt, lautet:
/bin/ksh myprog
Man kann in die erste Zeile des Shellscripts nach dem Programmnamen auch Aufrufoptionen für das entsprechende Programm angeben (jedoch nur ein Wort!). Will man zum Beispiel erreichen, daß die ksh ausgeführten Befehl auf der Standardausgabe anzeigt, schreibt man in die erste Zeile des Scripts:
#!/bin/ksh -x
Dies erweist sich bei der Fehlersuche in Shellscripts als sehr nützlich. Noch ausführlichere Informationen erzeugt der Aufruf
#!/bin/ksh -xv
Fehlt eine Programmangabe in der ersten Zeile des Scripts, so wird zur Interpretation die Datei /bin/sh verwendet. /bin/sh ist die ursprünglichste UNIX-Shell. Sie wird heute fast nur noch zur Interpretation von Scripts eingesetzt, da sie wenig Komfort bietet, aber auf jedem UNIX-System vorhanden ist.
Shell-Prozeduren können beim Aufruf Parameter mitgegeben werden; auf diese kann in der Prozedur mit $1, $2, ... (je nach Position) zugegriffen werden. Die Variable $0 ist mit dem Namen der Prozedur vorbelegt. Werden einer Prozedur mehr als 9 Parameter zugewiesen, so sind die Parameter ab Position 10 mit ${10}, ${11} ... anzusprechen. Die Variable $* steht für alle übergebenen Parameter. Die Anzahl der übergebenen Parameter steht in der Variablen $#.
Der Befehl shift verschiebt die Parameterliste um einen
Wert nach links.
Ein Verschieben der Parameter nach rechts ist nicht möglich.
Ihre Leistungsfähigkeit verdanken die Shell-Prozeduren zum großen Teil der Möglichkeit, ihren Ablauf mittels verschiedener Kontrollstrukturen zu steuern. Diese entsprechen in ihrer Arbeitsweise in etwa jenen, wie sie auch in den meisten prozeduralen Programmiersprachen vorhanden sind.
Natürlich können die folgenden Befehle auch interaktiv eingegeben werden, das wird aber abgesehen von for und if zu Testzwecken eher selten gemacht.
Ausgeführt wird die erste Kommandofolge, deren Musterangabe zu der im case-Ausdruck angegebenen Zeichenkette paßt. In der Musterangabe sind alle bereits für Dateinamen beschriebenen Formen der Kurzschreibweise verwendbar.
Wird als Muster eine Variable verwendet, die leer sein kann, so
sollte sie in "" eingefaßt werdedn, um einen
Syntaxfehler (kein Wort zwischen case und in) zu
vermeiden.
Der Variablen werden der Reihe nach alle angegebenen Argumente
zugewiesen. Fehlt die Angabe einer Liste, so werden statt dessen die
Aufrufparameter der Prozedur verwendet.
Eine evtl. angegebene Liste muß auf der selben Zeile stehen wie das Wort for. Wenn sie jedoch zu lang ist, kann man einen Zeilenumbruch verstecken (Backslash, siehe 10.3.1)
Die folgenden beiden Shell-Prozeduren haben die Aufgabe, im aktuellen Verzeichnis nach Dateien zu suchen, die mit den Zeichen .f (Zeichen für Fortran-Programme) enden.
Die erste Prozedur:
#Prozedur filecheck echo "$0 gestartet" params=`ls` # der Variablen params wird das Inhaltsverzeichnis des # aktuellen Directories zugewiesen echo $params sh for_detecter $params #Aufruf der Shell-Prozedur for_detecter echo "das wars" Die zweite Prozedur: #Prozedur for_detecter (wird von filecheck aufgerufen) echo "$*" while [ $# -ne 0 ] # solange die Anzahl der Parameter >0 do echo "$1" case "$1" in *.f) echo "Oh weh, ein Fortran-Programm";; #Extension .f *) echo "gut so";; #andere Dateinamen esac shift #Schiebe die Parameterliste nach links done echo "Ende der Unterprozedur"
Das Kommando sh filecheck führt zu folgender Ausgabe:
filecheck gestartet aligatormaximus.f filecheck for_detecter giraffe.c never.f no_name.c aligatormaximus.f filecheck for_detecter giraffe.c never.f no_name.c aligatormaximus.f Oh weh, ein Fortran-Programm filecheck gut so for_detecter gut so giraffe.c gut so never.f Oh weh, ein Fortran-Programm no_name.c gut so Ende der Unterprozedur das wars