Script-Programmierung

Verzweigungen

  1. if-Verzweigung
  2. case-Mehrfachentscheidung

Für Programmverzweigungen innerhalb der Scripte bietet die bash zwei sehr leistungsfähige interne Kommandos. Mit dem if-then-elif-then-else-fi-Konstrukt kann man einfache Entscheidungen oder komplexe Mehrfachentscheidungen in die eigenen Scripte aufnehmen. Das Kommando case bietet eine Möglichkeit, eine Mehrfachentscheidung mit Hilfe einfacher regulärer Ausdrücke zu realisieren.

  1. if-Verzweigung

    Mit dem if-Befehl kann man unterschiedlich komplexe Verzweigungen realisieren. Der Verzweigungsblock wird mit dem internen Schlüsselkommando if eingeleitet und mit dem Schlüsselwort fi wieder abgeschlossen. Alle Anweisungen dazwischen gehören dem Verzweigungsblock an. Verzweigungen können beliebig oft ineinander geschachtelt werden. Der if-Befehl erwartet ein einzelnes Kommando oder eine Kommandoliste, die durch die bash ausgeführt wird. Je nachdem, welchen exit-status das Kommando liefert, wird die Verzweigung im true- oder false-Zweig abgearbeitet. Der true-Zweig, wird jeweils mit then eingeleitet und durch das zugehörige elsif, else oder fi der Verzweigung wieder abgeschlossen. Mit elsif kann eine Mehrfachentscheidung definiert werden. Diese erwartet, ebenso wie if ein Kommando, dessen exit-status darüber entscheidet, ob der nach then folgende Anweisungsblock ausgeführt wird oder nicht. Mit else kann eine Alternative definiert werden, die ausgeführt wird, wenn alle vorhergehenden Entscheidungen der Verzweigung ein false ergeben haben. Als erstes, zwei einfache Beispiele zum Einsatz des if-Befehls:
    Das erste Script testet, ob der eingegebene Benutzername root ist und gibt eine entsprechende Meldung aus. Das Kommando [ führt einen Stringvergleich durch und gibt einen entsprechenden exit-status zurück. Es ist dabei zu beachten, dass es sich bei den eckigen Klammern um Kommandonamen !!!! handelt, sie müssen deshalb von Trennzeichen (Leerzeichen, Tabulatoren, Semikola) umgeben sein. Die Einrückungen in den Scripten dienen nur der Lesbarkeit des Scriptes und werden durch die bash ignoriert.
    #!/bin/bash
    read -p "Bitte geben Sie Ihren Benutzernamen ein:"  NAME
    if [ "$NAME" = "root" ]
    then
     echo "Hallo root!"
     echo "Nur root ist gut."
    fi
    if [ "$NAME" != "root" ]
    then
     echo "Hallo ${NAME}!"
     echo "Ich rede nur mit root."
    fi
    Die gleiche Funktionsweise, nur etwas übersichtlicher erreicht man durch die Verwendung der Alternativverzweigung else. Das Kommando nach if muss mit einem <newline> oder einem Semikolon abgeschlossen werden. Im zweiten Fall kann then dann auf der selben Zeile nach einem Leerzeichen folgen.
    #!/bin/bash
    read -p "Bitte geben Sie Ihren Benutzernamen ein: " NAME
    if [ "$NAME" = "root" ]; then
     echo -e "Hallo root!\nNur root ist gut."
    else
     echo -e "Hallo ${NAME}!\nIch rede nur mit root."
    fi
    Das dritte Script soll testen, ob eine Netzwerkschnittstelle im System verfügbar ist.
    #!/bin/bash
    DEV=${1:-eth0}
    if [ "$UID" = "0" ]; then
     if ifconfig $DEV > /dev/null 2>&1 ; then
      echo "Im System ist eine Netzwerkschnittstelle $DEV verfuegbar!"
      if IP=$( ifconfig $DEV | egrep "inet addr:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" ); then
       IP=${IP%% Bcast*}
       IP=${IP##*inet addr:}
       echo "Die Schnittstelle $DEV hat die IP-Adresse $IP"
      else
       echo "Die Schnittstelle $DEV hat keine IP-Adresse!"
      fi
     else
      echo "Im System ist keine Netzwerkschnittstelle $DEV verfügbar!"
     fi
    else
     echo "Dieses Script darf nur root ausfuehren!"
    fi
    In diesem Script wurden nun drei if-Verzweigungen ineinander verschachtelt. Zuerst wird eine Variable DEV mit Hilfe einer Parameterexpansion definiert. Diese testet, ob dem Script ein Parameter ($1) mit dem Namen der Schnittstelle (z.B. eth0, eth1, wlan0, ra0 etc) beim Start mit übergeben wurde. Wenn nicht, so wird die Variable mit dem Defaultwert eth0 geladen, ansonsten mit dem Inhalt des Positionsparameters 1. Die erste if-Verzweigung testet wieder mit dem Kommando [, ob die shell-Variable UID eine Null enthält, also ob root das Script ausführt. Die zweite if-Verzweigung ruft das Kommando ifconfig auf und testet mit Hilfe des exit-status von ifconfig, ob eine entsprechende Ethernetschnittstelle im System vorhaden ist oder nicht. Dabei wird der Name der Schnittstelle aus dem Inhalt der Variablen DEV expandiert, d.h. je nachdem, welcher Name in DEV gespeichert wurde, wird dieser an ifconfig als Parameter übergeben. Mit Hilfe einer Ausgabeumleitung werden die Ausgaben von ifconfig unterdrückt und nach /dev/null geleitet. Die dritte if-Verzweigung nutzt eine Kommandosubstitution einer Liste. Durch das Konstrukt IP=$( kommandos ) wird das Kommando durch die bash ausgeführt und die Ausgaben von Standardout in der Variable IP gespeichert. Das Kommando selbst ist eine Liste, bestehend aus den Kommandos ifconfig und egrep, die durch eine Pipe verbunden sind. Es wird also zuerst das Kommando ifconfig ausgeführt und die Ausgabe von Standardout an den Standardin von egrep weitergeleitet. egrep sucht nun in den Daten nach einem String, der mit "inet addr:" beginnt und dem eine IP-Adresse folgt, die aus vier Paketen von ein- bis dreistelligen Zahlen durch Punkte getrennt, besteht. Wird ein solcher String gefunden, so wird dieser von egrep ausgegeben und ein exit-status 0 (Null) erzeugt. Der exit-status wird von if getestet und die Ausgabe landet in der Variable IP. In den folgenden zwei Zeilen werden aus dem String noch die Broadcast und Netzwerkmaske, sowie der Eintrag "inet addr:" durch Parameterexpansion entfernt, sodass die Variable nur noch die IP-Adresse enthält. Der Rest des Scriptes besteht nur noch aus den entsprechenden Ausgaben und Verzweigungsalternativen. Der Aufruf des Scriptes kann nun unterschiedliche Reaktionen hervorrufen. Wenn dieses Script mit Ausführungsrechten nur für root ausgestattet ist, so ist es nur root erlaubt es zu starten. Das kann dann folgende Ergebnisse liefern:
    Das System besitzt in diesem Fall nur eine eth0 und eine wlan0 Schnittstelle. Die wlan0-Schnittstelle ist voll konfiguriert, für eth0 wurde nur das Kernelmodul geladen, eine IP wurde noch nicht zugewiesen.
    bash-2.05b$ ./test
    Im System ist eine Netzwerkschnittstelle eth0 verfuegbar!
    bash-2.05b$
    Mit Parameter eth1
    bash-2.05b$ ./test eth1
    Im System ist keine Netzwerkschnittstelle eth1 verfuegbar!
    bash-2.05b$
    Mit Parameter wlan0
    bash-2.05b$ ./test wlan0
    Im System ist eine Netzwerkschnittstelle wlan0 verfuegbar!
    Die Schnittstelle wlan0 hat die IP-Adresse 192.168.1.208
    bash-2.05b$
    Sollte das Script auch die Ausführung durch einen Benutzer zulassen, so erfolgt folgende Reaktion:
    bash-2.05b$ ./test eth1
    Dieses Script darf nur root ausfuehren!
    bash-2.05b$
    Zum Abschluss nun noch ein Script mit einer Mehrfachauswahl. Das Script erwartet als Parameter eine Zahl von 1..9 als Platzierung, wobei für die Plätze 1..3 die Worte Gold, Silber oder Bronze ausgegeben werden. Ist der Parameter keine gültige Platzierung, so wird eine Fehlermeldung erzeugt.
    #!/bin/bash
    if echo $1 | egrep "^[1-9]{1}$" > /dev/null 2>&1; then
     if [ $1 -eq 1 ]; then
      echo "Gold"
     elif [ $1 -eq 2 ]; then
      echo "Silber"
     elif [ $1 -eq 3 ]; then
      echo "Bronze"
     else
      echo "Platz $1"
     fi
    else
     echo "Sie haben keine gueltige Platzierung angegeben!"
    fi

    Zuerst wird in der ersten if-Verzweigung wieder eine Kommandoliste ausgeführt. Das echo-Kommando gibt den Inhalt des ersten Parameters $1 auf Standardout aus, der über eine Pipe an egrep übergeben wird. Das Kommando egrep testet nun, ob es sich um eine Zeile handelt, die nur eines der Zeichen 1..9 enthält. Da nur der exit-status von egrep interessiert, werden alle Ausgaben des Kommandos nach /dev/null umgeleitet. Wenn egrep nun mit dem exit-status 0 (Null) einen Treffer signalisiert, verzweigt if in den Scriptteil hinter then und es wird die Mehrfachauswahl getestet. Da sicher gestellt ist, dass $1 nur Werte von 1..9 enthält, habe ich im Kommando [ hier mal einen arithmetischen Vergleich und keinen Stringvergleich benutzt.

  2. case-Entscheidungen

    Mit dem case-Befehl können auf einfache Weise Mehrfachentscheidungen realisiert werden. Case erwartet dabei eine Variable und scannt den Variableninhalt nach verschiedenen Suchmustern. Das Suchmuster selbst kann dabei auch die Jokerzeichen *,? und [...] enthalten. Mehrere Muster können durch | mit einem logischen Oder verknüpft werden. Es können beliebig viele Suchmuster festgelegt werden. Diese Suchmuster werden innerhalb der Klammerung case Variable in <Suchmusterdefinitionen> esac festgelegt. Dazu wird hinter dem Suchmuster eine schliessende runde Klammer ) eingefügt, die die Liste einleitet, die beim Suchtreffer ausgeführt werden soll. Die Liste kann sich über mehrere Zeilen erstrecken und wird mit einem Doppelsemikolon ;; abgeschlossen. Danach kann das nächste Suchmuster folgen. Case scannt den Inhalt der Variable mit den definierten Mustern und bei der ersten Übereinstimmung wird die entsprechende Liste hinter der Musterdefinition ausgeführt und die case-Verzweigung dann verlassen. Weitere Muster werden also nicht! getestet, auch wenn sie einen Treffer ergeben würden. Das obige Programmbeispiel kann mit dem case-Konstrukt wie folgt umgesetzt werden:

    #!/bin/bash
    case "$1" in
     1) echo "Gold" ;;
     2) echo "Silber" ;;
     3) echo "Bronze" ;;
     [4-9]) echo "Platz $1" ;;
     "")
      echo "Sie haben keine Platzierung angegeben!"
      echo "Bitte geben Sie eine Zahl von 1-9 als Platzierung an!"
     ;;
     *) echo "Sie haben keine gueltige Platzierung angegeben!" ;;
    esac

    Es wird der Parameter in der Variable $1 nach sechs Mustern gescannt. Die ersten drei Muster passen nur, wenn die Variable genau die Zeichen 1,2 oder 3 enthalten. Also auch eine 11 ergibt keinen Treffer, da sie zwei Ziffern enthält. Das vierte Muster [4-9] definiert einen Bereich an gültigen Zeichen. Alle Zeichen von 4 bis 9 führen zu einem Treffer. Das fünfte Muster ergibt einen Treffer, wenn die Variable leer ist. Die auszuführende Liste erstreckt sich in diesem Beispiel über mehrere Zeilen, ich habe das durch die Einrückungen kenntlich gemacht. Die Anführungszeichen sind Quotungszeichen, die bei der Ausführung durch die bash entfernt werden, wenn das Muster solche Quotungszeichen enthalten soll, dann müssen sie gequotet werden. Das sechste Muster * akzeptiert jede beliebige Zeichenkombination oder kein Zeichen. Wenn kein Parameter übergeben wird und $1 damit leer ist, gibt es in Muster sechs jedoch keinen Treffer, weil dieser Fall ja schon mit Muster fünf abgetestet wird.