next up previous contents index
Next: Parameter Up: bash Previous: Listen

Gruppen und Kontrollstrukturen: Blöcke, Schleifen, Verzweigungen, Funktionen

 

Einzelne Kommandos oder Listen können auf verschiedene Weisen weiter zu Gruppen zusammengefaßt werden:

{ Liste ;}
Mit den geschweiften Klammern werden die Kommandos der Liste zu einer einfachen Gruppe zusammengefaßt. Diese Klammerung entspricht der in mathematischen Ausdrücken und dient der Assoziierung nieder- oder gleichrangig verknüpfter Kommandos.

Die Operatoren zur Shellprogrammierung unterscheiden sich in Rang und Assoziativität. Prinzipiell wird eine Kommandozeile von links nach rechts abgearbeitet. Die Zusammenfassung in UND- oder ODER-Listen hat gegenüber der Auflistung mit Semikolon oder & Vorrang. Die Zusammenfassung einfacher Kommandos in Pipelines hat wiederum größere Assoziativität als die Listen. Die beiden Zeilen

{ cat /etc/gettydefs || cat /etc/gettytab; }| grep 38400
cat /etc/gettydefs || cat /etc/gettytab | grep 38400
unterscheiden sich syntaktisch nur in der Klammerung, die größere Assoziativität der Pipeline führt in der zweiten Zeile aber zu einer impliziten Klammerung der beiden letzten Kommandos. Die erste Zeile schreibt die erste existierende der beiden angegebenen Dateien in den Standardausgabekanal, der durch eine Pipeline dem grep-Kommando zur Auswertung übergeben wird. Die zweite Zeile schreibt entweder die Datei /etc/gettydefs auf den Bildschirm (Standardausgabe), oder sie übergibt die Datei /etc/gettytab dem grep-Kommando.

Die geschweiften Klammern trennen selbst keine Kommandos (siehe oben). Aus diesem Grund ist ein Leerzeichen zwischen der einleitenden Klammer und dem ersten Kommando der eingeschlossenen Liste notwendig. Um die Trennung zum ersten Kommando nach der abschließenden Klammer eindeutig anzugeben, ist es sinnvoll, die eingeklammerte Liste immer mit einem Semikolon abzuschließen. Im Gegensatz zur Bourne-Shell wird in dem Beispiel oben das Kommando korrekt interpretiert, wenn das Semikolon weggelassen wird, weil die der Klammer folgende Pipeline die Kommandos trennt.

Im Unterschied zur Bourne-Shell wird bei der bash auch in dem Beispiel oder bei einer Ausgabeumlenkung für die komplette Klammer keine Subshell erzeugt.

( Liste)
Durch runde Klammerung werden die Kommandos einer Liste in einer eigenen (Shell-) Umgebung ausgeführt. Wenn von den Kommandos der Liste Veränderungen an der Shellumgebung vorgenommen werden, sind diese außerhalb der eingeklammerten Gruppe nicht sichtbar. Wenn zum Beispiel innerhalb einer so geklammerten Gruppe das Verzeichnis gewechselt wird, beziehen sich alle weiteren Kommandos innerhalb der Gruppe auf dieses Verzeichnis; außerhalb der Klammerung bleibt das ``alte'' Verzeichnis aktuell. Beispiel:

cd /
$ (cd /usr/bin; ls e*); ls -F
egrep    elvis    elvprsv  elvrec   env      ex       expand
bin/         etc/         lost+found/  root/        usr/
boot/        home/        mnt/         sbin/        var/
dev/         lib/         proc/        tmp/         vmlinuz
$ _
erzeugt eine Subshell, in der aus dem aktuellen Wurzelverzeichnis in das Verzeichnis /usr/bin gewechselt wird. Hier können beispielsweise alle Dateien angezeigt werden, deren Name mit einem `e' beginnt. Mit den schließenden Klammern wird auch die Subshell beendet. Das darauffolgende Listing findet wieder im ursprünglich aktuellen Verzeichnis statt.

for Name [ in Wort ] do Liste done
  Mit dieser Konstruktion stellt die bash die von vielen Programmiersprachen bekannte und bewährte for-Schleife bereit. Mit Name wird eine Shellvariable definiert, die in jedem Schleifendurchlauf einen neuen Wert erhält. Die Anzahl der Schleifendurchläufe entspricht der Anzahl der vorhandenen Werte.

Die Werte werden normalerweise nach dem Schlüsselwort in übergeben. Dazu können mehrere Wörter angegeben werden, die von der Shell erweitert werden (-> Seite gif). Für jeden Durchlauf wird ein Token in der Variablen Name übergeben.

Wenn der ` in Wort' Teil fehlt, wird die Liste für jeden gesetzten Positionsparameter (-> Seite gif) einmal ausgeführt.

Die interaktive Eingabe einer for-Schleife wird (wie die Eingabe jedes anderen aus mehreren Teilen bestehenden Konstrukts) von der Shell unterstützt, indem sie einen sekundären Prompt ( PS2) zur Eingabe einer Schleife über mehrere Zeilen anbietet. Dieser sekundäre Prompt wird ausgegeben, solange die Schleife nicht mit done abgeschlossen ist.

Als Status wird der Rückgabewert des letzten ausgeführten Kommandos zurückgegeben. Wenn kein Kommando ausgeführt wurde, ist der Status Null.

In dem folgenden Beispiel werden im aktuellen Verzeichnis alle Dateien mit der Endung `foo' umbenannt, so daß sie auf `bar' enden. 

$ for i in *.foo; do
> base=`basename $i .foo`
> mv $i $base.bar
> done
$ _
Das basename-Kommando ist auf Seite gif beschrieben. Die Zuweisung der Ausgabe dieses Kommandos an die Variable base findet durch ``Kommandosubstitution'' statt. Diese Shelloperation wird auf Seite gif beschrieben. Diese Operation wird durch die ``Backquotes'' ausgelöst, nicht durch Hochkomma.

Die bash bietet mit ihrer Parametererweiterung eine andere schöne Methode, die Endung vom Dateinamen zu trennen: mit der Konstruktion base=${i%.foo} (-> Seite gif). Das spart den Aufruf eines externen Kommandos und verbessert damit die Performance.

Wenn eine Variable innerhalb einer for-Schleife verändert wird, ist der neue Wert auch außerhalb der Schleife sichtbar. Die bash verhält sich damit POSIX-1003.2-konform. Eine Ausnahme kann für die Zählvariable erreicht werden, indem das Kommando set -l gegeben wird. In diesem Fall wird die Zählvariable nach dem letzten Durchlauf auf den Wert vor Beginn der Schleife zurückgesetzt.

while Liste do Liste done
  Von anderen Programmiersprachen ebenso bekannt ist die while ... do ... done-Schleife. Hier wird der Schleifenkörper ` do Liste done' so lange wiederholt, bis die in ` while Liste' formulierte Bedingung falsch ist. Das ist der Fall, wenn das letzte Kommando der while Liste einen Status ungleich Null liefert.

Bei der interaktiven Eingabe einer while-Schleife wird so lange der sekundäre Prompt ( PS2) ausgegeben, bis die Schleife mit done abgeschlossen ist.

Der Status der while-Schleife ist gleich dem Status des letzten Kommandos des ` do-Teils' oder Null, wenn kein Kommando ausgeführt wurde.

Im folgenden Beispiel wird die while-Schleife benutzt, um die Unterverzeichnisse z. B. des Verzeichnisses /usr/local/man anzulegen.

$ declare -i zahl=1
$ while [ $zahl -lt 10 ]
> do
> mkdir man$zahl
> mkdir cat$zahl
> zahl=$[zahl+1]
> done
$ _
Die Deklaration von zahl als Integer-Variable ist an dieser Stelle nicht notwendig, weil die Zuweisung einer Zahl und die Verwendung in arithmetischen Ausdrücken diese Interpretation implizieren.

Zur Inkrementierung der Zählvariablen wird die Arithmetische Erweiterung benutzt, die auf Seite gif beschrieben ist. Die bash erlaubt ab Version 1.13 auch die direkte Inkrementierung einer Integer-Variablen durch den ` +=' Zuweisungsoperator in einer arithmetischen Erweiterung oder mit dem let-Shellkommando. Beide Varianten sind in der Standard-Bourne-Shell nicht vorgesehen.

until Liste do Liste done
  Die until-Schleife entspricht der while-Schleife mit dem Unterschied, daß der do-Teil so lange ausgeführt wird, wie das letzte Kommando der ` until Liste' einen Status ungleich Null liefert.

if Liste then Liste [ elif  Liste then Liste ... ] [ else Liste ] fi
Mit der if-Konstruktion werden die Kommandos der then Liste unter der Bedingung ausgeführt, daß das letzte Kommando der if Liste ``wahr'' ist, also einen Status Null liefert.

Mit der optionalen elif-Konstruktion können beliebig lange if- else-Ketten erzeugt werden. Wenn der ` elif Liste'-Teil des letzten elif nicht Null liefert, wird der abschließende else-Teil bearbeitet.

Wie bei den Schleifen wird auch bei der interaktiven Eingabe einer if-Anweisung der sekundäre Prompt ausgegeben, bis die Konstruktion mit einem fi abgeschlossen ist.

Der Status der gesamten if-Konstruktion ist gleich dem Status des zuletzt ausgeführten Kommandos, oder Null, wenn kein Kommando ausgeführt wurde.

Das folgende Beispiel zeigt, wie in der Initialisierungsdatei ~/.bashrc zwischen einer Shell im xterm und den Textbildschirmen unterschieden werden kann.

if [ $WINDOWID ]; then
   TERM=xterm
   export XDVIFONTS=/usr/TeX/lib/tex/fonts/%f.%d%p
   export OPENWINHOME=/usr/openwin
   export PAGER=/usr/X386/bin/xless
else
   export PAGER=/usr/bin/less
fi
Ein weiteres Beispiel zeigt, wie in einem Shellscript unterschieden werden kann, ob die aufrufende Shell interaktiv arbeitet oder nicht.
if [ "${-#*i}" = "$-" ]; then
   echo Die Shell arbeitet nicht interaktiv
else
   echo Die Shell arbeitet interaktiv
fi
Der in diesem Beispiel benutzte Ausdruck ["${-#*i}" = "$-" ] ist ein schönes Beispiel für die Parametererweiterung, wie sie auf Seite gif erklärt ist. Im Spezialparameter - sind die Optionsflags der Shell gespeichert. Durch die ``Erweiterung'' ${-#*i} wird aus dieser Zeichenkette ein ` i' (und alle Zeichen davor) entfernt, wenn es enthalten ist. Der umschließende test (-> Seite gif) vergleicht die so eventuell verkürzte Zeichenkette mit dem Original. Wenn sie gleich sind, ist die ` i'-Option der interaktiven Shell nicht gesetzt.

Die Anführungszeichen sind in dem Beispiel notwendig, weil die Shellvariable ` -' auch leer sein kann. In diesem Fall würde sie ohne die Anführungszeichen aus der Kommandozeile entfernt, was in dem Vergleich zu einem verdeckten Syntaxfehler und einem falschen Ergebnis des Tests führen könnte.

case Wort in [ Muster  [ | Muster ... ] ) Liste ;; ... ] esac
Mit der case-Anweisung können leicht Verzweigungen programmiert werden, bei denen viele Fälle unterschieden werden.

Das case Wort wird von der bash erweitert, dann wird die daraus entstandene Zeichenkette mit den Mustern verglichen und bei Übereinstimmung die Liste von Kommandos ausgeführt. In den Suchmustern können reguläre Ausdrücke wie bei der Pfadnamenerweiterung (z. B. ` *' und ` ?') verwendet werden.

Wenn ein übereinstimmendes Muster gefunden wurde, wird die case-Anweisung beendet und nicht nach weiteren Übereinstimmungen gesucht.

Der Status ist Null, wenn keine Übereinstimmung gefunden wurde. Sonst wird der Status des zuletzt ausgeführten Kommandos der Liste übergeben.

Das dem letzten Beispiel zugrunde liegende Problem kann auch mit der case-Anweisung gelöst werden. Diese Lösung ist zwar kein typisches Beispiel für eine Vielfachverzweigung, sie bietet sich aber wegen der Auswertung regulärer Ausdrücke durch case in der Praxis eher an als das oben gegebene Beispiel.

case $- in
        *i\*) echo hier sollte nach der Zeichenkette 'i*'
              echo gesucht werden.
              echo   *****FEHLER***** in der bash-1.12.;;
        *i*)  echo Die Shell arbeitet interaktiv.;;
        *)    echo Die Shell arbeitet nicht interaktiv.;;
esac
In der bash Version 1.12 konnte mit der case-Anweisung nicht nach regulären Ausdrücken selbst gesucht werden, die Quotierung der Wildcards wurde ignoriert. Ab Version 1.13 ist dieser Fehler behoben.

select Name [in Wort...; ] do Liste done
Die select- Kontrollstruktur bietet eine Kombination aus menügesteuerter Verzweigung und Schleife.        

Der in Wort-Teil wird erweitert und die so generierten Wörter als numerierte Liste (Menü) auf dem Standardfehlerkanal ausgegeben. Mit dem PS3-Prompt wird daraufhin eine Eingabe von der Tastatur gelesen. Eine leere Eingabe führt zu einer erneuten Anzeige des Menüs.

Wenn ein Wort aus der Liste durch seine Nummer bestimmt wird, führt die bash die Kommandos der do Liste aus und stellt dabei das ausgewählte Wort in der Variablen Name zur Verfügung. Wird in der Eingabezeile keine passende Zahl übergeben, ist Name leer, die Eingabezeile ist aber in der Variablen REPLY gespeichert.

Menüteil und Ausführung der Liste werden so lange wiederholt, bis die Schleife mit break oder return verlassen wird. Es ist möglich, mit CONTROL-D das Menü unmittelbar zu verlassen.

Wenn der in Wort-Teil fehlt, werden stattdessen die Positionsparameter verwendet.

[ function ] Name () { Liste}
    definiert eine neue Scriptfunktion Name.

Scriptfunktionen sind Teile eines Shellscripts oder einer Initialisierungsdatei für eine interaktive Shell, die komplett in der Shellumgebung gespeichert werden. Wenn ein Kommando in der Kommandozeile mit einer solchen Funktion übereinstimmt, wird die unter dem Funktionsnamen gespeicherte Liste von Kommandos ausgeführt. Es wird dazu kein neuer Prozeß erzeugt.

Im Unterschied zu alias-Synonymen können Scriptfunktionen Argumente verarbeiten. Wenn die Scriptfunktion mit Argumenten aufgerufen wird, werden diese Argumente der Funktion als Positionsparameter übergeben. Der Spezialparameter ` #' wird aktualisiert. Der Positionsparameter ` 0' bleibt allerdings unverändert. 

Mit dem local-Shellkommando ist es möglich, lokale Variablen für Scriptfunktionen zu erzeugen. Normale Shellvariablen sind ``global'', sind also in der gesamten Shell uneingeschränkt sichtbar.

Wenn in einer Scriptfunktion das Shellkommando return auftaucht, wird die Funktion beendet und mit der dem Aufruf folgenden Zeile des Shellscripts oder der interaktiven Eingabe fortgesetzt. Die Positionsparameter und der Spezialparameter ` #' werden auf ihre Werte vor dem Funktionsaufruf zurückgesetzt.

Eine Liste der definierten Scriptfunktionen erhält man in einer interaktiven Shell mit der ` -f'-Option zu den Shellkommandos declare oder typeset. Die Scriptfunktionen werden nur dann an alle Untershells weitergereicht (und stehen nur dann auch in diesen Shells zur Verfügung), wenn sie mit der Shellfunktion export unter der Option ` -f' für den Export bestimmt wurden.

Scriptfunktionen können rekursiv aufgerufen werden. Es gibt keine Begrenzung für die Anzahl der rekursiven Aufrufe.

 

next up previous contents index
Next: Parameter Up: bash Previous: Listen



Linux Anwenderhandbuch -- Copyright 1993, 1994, 1995 S. Hetze, D. Hohndel, O. Kirch, M. Müller