8.8    Schleifen

Sie verfügen nun über das notwendige Grundwissen, um Schleifen zu verstehen. Einer Schleife wird eine Bedingung übergeben. Ist diese erfüllt, werden ihre Anweisungen so lange ausgeführt, bis diese Bedingung nicht mehr erfüllt ist.

Eine Bedingung ist sowohl in einer Schleife als auch in einer case-Verzweigung oder if-Anweisung immer wahr, wenn sie den Wert 1 ergibt. Probieren Sie einmal folgende Anweisung aus:

user$ if [ 1 ]
then
     echo wahr
fi
wahr // diese Zeile ist bereits die Ausgabe der if-Anweisung
user$

Listing 8.39     Eine immer wahre if-Anweisung

Bei einer Schleife würde der Befehl echo wahr so lange ausgeführt, bis die Bedingung der Schleife nicht mehr erfüllt wäre.

8.8.1    Die while-Schleife

Die Syntax der while-Schleife ist im folgenden Listing abgebildet. Die Anweisungen werden mit dem do-Schlüsselwort eingeleitet und mit done beendet.

while [ Bedingung ]
do
    Anweisung A
    Anweisung B
    …
done

Listing 8.40     Syntax der while-Schleife

Doch in der Praxis lernt man bekanntlich am besten. Im Folgenden wird eine Endlosschleife erstellt, die alle 30 Minuten die verfügbare Kapazität der Datenträger überprüft. Sinkt die verbliebene freie Kapazität auf unter 5 % (der df-Befehl gibt in diesem Fall einen Wert von über 95 % für die genutzte Kapazität an), wird eine Meldung ausgegeben. Das Prozentzeichen wird mit dem sed-Programm herausgefiltert. awk und sed haben wir in Kapitel 6 besprochen.

#!/bin/bash

while [ 1 ]
do
     df -h | grep -v '[Uu]se' | sed s/\%// | \
     awk '{
          if($5>95)
               print $1 " is almost full. ("$5"%)"
     }'
     sleep 1800
done

Listing 8.41     Endlosschleife

8.8.2    Die for-Schleife

Die for-Schleife bietet gegenüber der while-Schleife einen Vorteil: Sie kann eine Liste von Werten durcharbeiten. Das heißt, die Schleife wird für jeden angegebenen Wert einmal durchlaufen. Dabei wird einer Variablen für jeden Schleifendurchlauf ein weiterer zuvor angegebener Wert zugewiesen, mit dem Sie innerhalb des Anweisungsblocks arbeiten können. Dies ist beispielsweise sehr nützlich, wenn eine Liste von Dateien verarbeitet werden soll.

for VAR in Werte
do
     AnweisungA
     AnweisungB
     …
done

Listing 8.42     Syntax der for-Schleife

Stellen Sie sich einmal vor, dass alle Benutzerverzeichnisse archiviert werden sollen. Die Benutzerverzeichnisse der Benutzer, die mit dem Buchstaben »A« beginnen, sind im Verzeichnis /home/a abgelegt, die derjenigen, die mit »B« beginnen, sind in /home/b/ untergebracht usw. Als Archivmedien stehen der Veranschaulichung halber – Achtung, eher praxisfern – USB-Sticks mit einer Kapazität von 2 GByte zur Verfügung und die Benutzer-Quotas beschränken sich pro Buchstabenverzeichnis auf genau diese Kapazität.

Mit der for-Schleife können wir nun ein simples Skript entwickeln, das jeweils ein Verzeichnis auf einem Stick sichert und Sie danach auffordert, den nächsten Stick einzustecken.

#!/bin/bash

USBSTICK=/dev/sde1
TARGET=/mnt/stick

for DIR in /home/*; do
    # Ist es ein Verzeichnis?
    if [ -d $DIR ]; then
        mount -t vfat $USBSTICK $TARGET
        if [ $? -eq 0 ]
        then
            cp -r $DIR $TARGET
            umount $TARGET
            echo "Bitte nächstes Medium einlegen."
        else
            echo "Mountvorgang schlug fehl!"
            exit
        fi
    fi
done

Listing 8.43     Ein einfaches Backup

Im Optimalfall sollte noch die verfügbare Kapazität auf dem Medium überprüft und mit dem tar-Kommando eine Komprimierung erzielt werden. Doch hätten wir dies gezeigt, wäre wohl das eigentliche Ziel, nämlich die Demonstration der for-Schleife, untergegangen.

8.8.3    seq – Schleifen mit Aufzählungen

Manchmal möchte man eine Schleife "uber eine Ziffernfolge durchlaufen oder benötigt aus irgendeinem anderen Grund eine Ziffernfolge. Dafür wurde das Programm seq geschrieben, das hier nur kurz angesprochen werden soll. Eine Parameterliste für seq finden Sie in der Kommandoreferenz.

Soll seq beispielsweise alle Zahlen von 1 bis 10 auflisten, dann ist dies ganz einfach. Übergeben Sie den Start- und Endwert:

$ seq 1 10
1
2
3
4
5
6
7
8
9
10

Listing 8.44     seq in Aktion

Möchten Sie seq in eine Schleife packen, so nutzen Sie am besten die Kommandosubstitution. Wollen Sie etwa einen bestimmten Satz Dateien löschen, dann wäre Folgendes eine Möglichkeit:

$ for i in `seq 1 10`; do
    rm "file$i"
done

Listing 8.45     seq in einer for-Schleife

8.8.4    until

Zu den bereits bekannten Schleifen kommt noch die until-Schleife hinzu. Diese neue Schleife kann als Negation der while-Schleife verstanden werden. Der Anweisungsblock der until-Schleife wird so lange ausgeführt, wie die Bedingung nicht erfüllt ist. Man kann praktisch die until-Schleife mit einer while !-Schleife gleichsetzen:

while [ ! 1 ]; do echo Test; done
until [ 1 ]; do echo Test; done
# oder noch kürzer:
until :; do echo Test; done

Listing 8.46     until und while !

8.8.5    break – Schleifen abbrechen

Um eine Schleife mitten im Durchlauf an einer beliebigen Stelle zu verlassen, setzen Sie dort das break-Schlüsselwort. Dies funktioniert sowohl mit while- als auch mit for- und until-Schleifen.

Da die select-Anweisung wie eine Schleife fungiert, können Sie sie durch (Strg) + (D) abbrechen, oder Sie integrieren eine Abbruchfunktion, in der Sie break verwenden. Letzteres ließe sich folgendermaßen umsetzen:

#!/bin/bash

echo "Was haben Sie für ein Haustier?"

select HAUSTIER in Hund Katze Beenden
do
   if [ "$HAUSTIER" = "Beenden" ]; then break; fi
   
   echo "Sie haben also ein/eine(n) $HAUSTIER"
   echo "Kann Ihr Haustier auch in Common-Lisp programmieren?"
done

Listing 8.47     select-Beispiel