Linux-Terminal-Basics 12: Schleifen mit for, while, until
Mit Schleifen kommt etwas Schwung in den Terminal-Alltag - und viel mehr Effizienz
Schleifen sind ein grundlegender Bestandteil jedweder Programmierung, aber auch allein für sich schon ziemlich nützlich. Kurz gesagt: Ihr könnt Befehle beliebig viele Male ausführen lassen, beispielsweise, um viele Dateien als Stapel zu verarbeiten. Oder Daten von vielen Programmen verarbeiten lassen. Oder Programme erst ausführen, wenn Bedingungen erfüllt sind Hmm, so ganz kurz geht das wohl doch nicht. .
In unserer Serie zu Linux-Terminal-Basics zeigen wir Euch, wie Ihr einige der wichtigsten Aufgaben auf der Kommandozeile erledigen könnt - vom Navigieren, über Dateioperationen, bis hin zu komplexen Suchaufträgen. Als Terminal verwenden wir Bash und auch wenn Linux im Vordergrund steht, funktioniert fast alles auch unter Windows. Und nun noch die Links zur Einleitung und zur Übersicht aller Artikel.
while und until
Deutlich bekannter ist die while-Schleife: Hier wird eine Aufgabe wiederholt ausgeführt, so lange die Bedingung erfüllt wird. Bei der until-Schleife wird die Aufgabe ausgeführt, bis die Bedingung erfüllt wird.
Zunächst zu while: Das Grundkonstrukt sieht so aus:
while Bedingung
do Kommandos
done
oder
while Bedingung; do Kommandos; done
Die Bedingung könnte so etwas sein wie "so lange a kleiner als 4". Aber die einfachste Form liefert wohl das Programm true: Dieses tut nichts - und das erfolgreich (steht so im Handbuch). Der Befehl true macht tatsächlich nichts, außer dem Terminal zurückzumelden, dass er erfolgreich war. Damit kann man sich eine Endlosschleife basteln:
while true
do echo Hallo!
sleep 1
done
Diese Schleife würde also prüfen, ob true erfolgreich ist und da es das immer ist, für die Ewigkeit im Sekundentakt "Hallo!" ausgeben.
Mit einer echten Bedingung könnte eine Schleife zum Beispiel so aussehen:
while (ping -c1 tutonaut.de)
do sleep 10
done
echo "Warnung - tutonaut.de offline"
Hier würde also mit ping geprüft, ob die URL tutonaut.de erreichbar ist. Falls ja, pausiert sleep das Skript 10 Sekunden und es beginnt von vorn. Sobald die Bedingung nicht mehr erfüllt wird, wird die Schleife beendet und eine Warnmeldung ausgegeben.
Und nun zu until: Erinnert Euch an die Endlosschleife von oben - hier aber mit until:
until true
do echo Hallo!
sleep 1
done
In diesem Fall würde was passieren? Gar nichts. Denn until läuft eben so lange bis die Bedingung erfüllt wird - und das ist hier eben sofort der Fall. Die Kommandos werden also gar nicht verarbeitet.
Analog zum ping-Beispiel von oben:
until (ping -c1 tutonaut.de)
echo "Die Seite ist online!"
done
Die Schleife läuft so lange, bis ping erfolgreich ist und meldet dann final den Erfolg. So könntet Ihr auch im eigenen Netzwerk im Auge behalten, ob zum Beispiel ein Medienserver startet.
So, und jetzt noch eine Portion Verwirrung:
while true; do echo foobar; done
until ! true; do echo foobar; done
Die beiden Schleifen machen exakt das Gleiche - endlos foobar ausgeben. Der Grund ist das ! vor true, das schlicht für "nicht" steht. Und da "nicht wahr" niemals wahr wird ... Endlosschleife halt.
Und als letzter Hinweis: Es gehen auch mehrere Bedingungen:
while ( true ; ping tutonaut.de ); do echo foobar; done
Mehrere Bedingungen setzt Ihr also per Semikolon getrennt in Klammern. Gibt es noch Luft für die for-Schleife?
for-Schleife
Im Allgemeinen werden while und until eingesetzt, wenn man keine Ahnung hat, wie oft die Schleife letztlich durchlaufen wird. Die for-Schleife geht eher von einer überschaubaren Menge an Durchläufen aus. Es wird eine Menge vorgegeben und die Kommandos laufen für jedes Element dieser Menge ein mal durch.
Das Grundskelett:
for Items (i) in Menge
do Kommandos
done
Wirklich schön: Die Menge kann einfach aus angegebenen Strings bestehen:
for i in Mirco Boris Christian
do echo "Hallo $i"
done
Und schon begrüßt uns die Shell in drei separaten Zeilen mit Namen - Danke, Hallo Bash. Wichtig ist in diesem Konstrukt die Variable i, die natürlich nicht i heißen muss, das ist nur das Standardbeispiel. Darüber sprecht Ihr jedenfalls das Element des jeweiligen Durchlaufs an. Überhaupt, gewöhnt Euch am besten schon jetzt gute Variablennamen an:
for vorname in Mirco Boris Christian
do echo "Hallo $vorname"
done
Das ist doch wesentlich verständlicher, oder?
Die Kommandos sind trivial, da steht Euch die ganze Bandbreite zur Verfügung - wie Ihr das jeweilige Element ansprecht wisst Ihr ja nun. Interessanter ist die Menge. Eine typische Frage ist: Wie lasse ich eine Schleife X mal durchlaufen? Eine mögliche Antwort:
for ziffer in $(seq 1 10)
do echo Es ist Ziffer $ziffer
done
Das Tool seq gibt schlicht Sequenzen aus, hier also 1 bis 10, was entsprechend 10 Durchläufe ergibt. Wie wäre es mal mit einem Praxisbeispiel:
for dokument in $(ls dateien/dokumente/*.txt)
cp $dokument /media/usb/backups/
done
Hier besteht die Menge also aus allen TXT-Dateien, die ls im Ordner gefunden hat. Dann wird jede Datei separat in einen Backup-Ordner kopiert - und schon habt Ihr eine erste wirklich nützliche Stapelverarbeitung.
In der Regel führen for-Schleifen ein Kommando mehrmals mit unterschiedlichen Optionen oder Daten aus. Es geht aber auch umgekehrt:
for prg in echo cowsay
do $prg "Hallo Welt"
done
In diesem Fall würde also der Text "Hallo Welt" von den Programmen echo und cowsay ausgegeben. Nützlich wäre das vielleicht zum Vergleichen von Ausgabeformaten.
Bonus: X-kleiner-als-Schleifen
Hier noch ganz kurz ein paar Beispiele für die gängigen So-lange-X-kleiner-als-10-ist-Schleifen. Zunächst mit while:
x=0
while [[ $x -lt 10 ]]
do echo $x ist kleiner als 10.
((x++))
done
Die Bedingung läuft hier über die doppelten eckigen Klammern, die einen Vergleichstest durchführen (es ist einer alternative Schreibweise für das Kommando "test"); -lt steht hier für lesser than/weniger als. Und das Hochzählen erledigt die doppelte runde Klammer, in der ++ wie üblich für +1 steht.
Statt der eckigen Klammern ginge auch die reguläre test-Schreibweise:
x=0
while $(test $x -lt 10)
do echo $x ist kleiner als 10.
((x++))
done
$(test ...) und [[...]] ist exakt dasselbe.
Mit until:
x=0
until [[ $x -eq 10 ]]
do echo $x ist kleiner als 10.
((x++))
done
Nur der Vergleichsoperator hat sich verändert, hier ist es jetzt -eq für equals/gleich.
Und jetzt mit for:
for (( x=0 ; x<10 ; x++ ))
do echo $x ist kleiner als 10.
done
Bei for läuft es etwas eleganter: In der doppelten runden Klammer stehen drei Ausdrücke, genauer gesagt arithmetische Ausdrücke. Die ersten beiden formulieren Bedingungen, die dritte eine Operation, sofern die Bedingungen erfüllt sind. Scheiss Wordpress: Der zweite Ausdruck ist x kleiner als 10 - aber das behämmerte Wordpress kriegt es nicht auf die Reihe, die spitze Auf-Klammer für kleiner-als zu setzen ... stattdessen ersetzt es automatisch mit dem was da steht - DAS wiederum kann ich hier außerhalb eines Code-Blocks nicht wiederholen, weil daraus automatisch die spitze Klammer würde ... Richtig wäre - im "Inline-Code-Block" geht das nämlich: x<10
Falls Ihr übrigens die Hilfen zu Schleifen sucht: Eigene Manuals und Hilfe-Ausgaben haben sie nicht, als Bestandteile der Bash sind sie aber im Bash-Manual dokumentiert, also:
man bash
So, genug für etwas, das sich Basics schimpft. Vielleicht heute mal keine weiteren CLI-Artikel lesen, sondern ... hmmm ... was fluffigeres ... vielleicht was zu einem klassischen Feminismus-Rätsel bei ChatGPT.
Bash-until ist für Programmierer etwas verwirrend, da es in den meisten anderen Programmiersprachen in sogenannten nachprüfenden Schleifen eingesetzt wird. In nachprüfenden Schleifen wird der Schleifenkörper mindestens ein Mal durchlaufen um nachher die Schleifenbedingung zu prüfen (tue-solange-bis). In Bash ist until jedoch eine vorprüfende Schleife. Dieser verwirrende Unterschied ist wohl auch der Grund, warum until auch selten verwendet wird. Ich konnte es bisher in keinem der Bash-Skripte aus den installierten Paketen finden, ganz im Gegensatz zu while.