Bash kennt zwei Arten von Schleifen:
Um über eine Liste von Elementen zu iterieren, verwenden wir die for-Schleife.
for varname in words; do COMMANDS; done
words wird durch Worttrennung und Dateinamenexpansion expandiert (was wir im Quoting-Konzept kennengelernt haben).
Jedes Wort wird der Reihe nach der Variable varname zugewiesen und die COMMANDS werden ausgeführt.
Hier sind einige Beispiele, die zeigen, wann die for-Schleife nützlich sein kann:
Einige Befehle für eine Gruppe von Dateien ausführen.
# source all the bash library files in the current directory
for file in ./*.bash; do
source "$file"
done
Einen Befehl für jeden Parameter eines Skripts oder einer Funktion ausführen.
for arg in "$@"; do
echo "argument: ${arg}"
done
Einen Befehl für jedes durch Whitespace getrennte Wort in der Ausgabe eines Befehls ausführen.
for i in $(seq 10); do
echo "${i} squared is $((i * i))"
done
Zu diesem Stil kommen später noch einige Warnungen.
Bash hat tatsächlich eine arithmetische Schleife. Das dürfte dir aus anderen Programmiersprachen bekannt vorkommen.
for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done
Die doppelten runden Klammern in Bash stehen für einen arithmetischen Kontext (mehr zur Arithmetik in Bash sehen wir in einem späteren Konzept).
Das obige Beispiel mit seq lässt sich so schreiben:
for ((i = 1; i <= 10; i++)); do
echo "${i} squared is $((i * i))"
done
Ein Anti-Pattern, das dir oft begegnet: über die Ausgabe von cat zu iterieren.
for line in $(cat some.file); do
do_something_with "$line"
done
Das ist aus zwei Gründen falsch.
Wie wir im Quoting-Konzept gelernt haben, lässt sich die Worttrennung mit der Variable IFS steuern und die Dateinamenexpansion abschalten.
Doch meist ist das eine fragile Lösung.
for ist die falsche Methode, um über die Zeilen einer Datei zu iterieren.
Eine Datei liest man idiomatischerweise mit einer while-Schleife.
Verwende while, um eine Folge von Befehlen zu wiederholen, solange eine Bedingung wahr ist.
while CONDITION_COMMANDS; do COMMANDS; done
Wie bei if (wie wir im Conditionals-Konzept gelernt haben, gibt es keine besondere Syntax für CONDITION_COMMANDS.
Der Exit-Status der Befehlsliste entscheidet über „wahr“ oder „falsch“.
Der Befehl break springt aus der Schleife heraus.
Die Ausführung geht mit dem Befehl weiter, der auf das abschließende done der Schleife folgt.
Der Befehl continue springt zur nächsten Iteration der Schleife.
Wie erwähnt ist eine while-Schleife die idiomatische Art, eine Datei zu lesen.
while IFS= read -r line; do
do_something_with "$line"
done < some.file
Der Inhalt der Datei wird der Schleife über die Umleitung < als Eingabe bereitgestellt.
Der Befehl read gibt „wahr“ zurück, wenn er eine vollständige Zeile aus seiner Eingabe lesen kann.
Wenn die Eingabe aufgebraucht ist oder die letzte Zeile ohne abschließenden Zeilenumbruch endet, gibt read „falsch“ zurück.
Die wirklich idiomatische Art, die Zeilen einer Datei zu lesen, selbst wenn die letzte Zeile nicht mit einem Zeilenumbruch endet, ist
while IFS= read -r line || [[ -n "$line" ]]; do ...
Die Option -r weist read an, Backslash-Sequenzen nicht zu ersetzen: Ein Backslash ist einfach ein normales Zeichen.
IFS= weist IFS nur für die Dauer des read-Befehls die leere Zeichenkette zu.
Dadurch wird die Worttrennung vorübergehend abgeschaltet, sodass führender oder abschließender Whitespace in der eingehenden Textzeile erhalten bleibt.
Das Konstrukt until wiederholt eine Folge von Befehlen, solange eine Bedingung falsch ist.
Die Schleife wiederholt sich, bis die Bedingung wahr wird.
until CONDITION_COMMANDS; do COMMANDS; done
until wird selten verwendet.
Öfter verwendet man eine „while not“-Schleife.
while ! CONDITION_COMANDS; do ...
Manchmal möchtest du eine Schleife endlos laufen lassen. Hier sind zwei Möglichkeiten dafür.
eine while-Schleife mit einer Bedingung, die immer einen Erfolgs-Exit-Status hat
while true; do ...
eine arithmetische for-Schleife mit leeren Ausdrücken
for ((;;)); do ...
Es gibt kein explizites do-while-Konstrukt, aber du kannst denselben Effekt erreichen:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done