if, case & Schleifen
Kontrollstrukturen in Bash: if mit Tests, case fuer Pattern Matching, for, while und typische Fallstricke bei den Vergleichs-Operatoren.
Inhaltsverzeichnis
Kontrollstrukturen in Bash
Bash hat klassische if, case und Schleifen - aber mit einigen Eigenheiten beim Test-Operator.
if / elif / else
#!/usr/bin/env bash
alter=18
if [[ $alter -ge 18 ]]; then
echo "Volljaehrig"
elif [[ $alter -ge 16 ]]; then
echo "Fast volljaehrig"
else
echo "Zu jung"
fi
Struktur:
if bedingung; then- mit Semikolon oder neue Zeileelif(ein Wort!)fischliesst (rueckwaerts gelesen: if)
[[ ... ]] - der moderne Test
Nutze immer [[ ... ]] statt [ ... ] (dem alten test-Kommando):
- Sicherer bei Leerzeichen in Variablen
- Erlaubt Regex-Matching mit
=~ - Erlaubt
<und>ohne Escape
Zahl-Vergleiche
[[ $a -eq $b ]] # equal
[[ $a -ne $b ]] # not equal
[[ $a -lt $b ]] # less than
[[ $a -le $b ]] # less or equal
[[ $a -gt $b ]] # greater than
[[ $a -ge $b ]] # greater or equal
String-Vergleiche
[[ "$s1" = "$s2" ]] # gleich
[[ "$s1" != "$s2" ]] # ungleich
[[ -z "$s" ]] # leer
[[ -n "$s" ]] # nicht leer
[[ "$s" == foo* ]] # Glob-Match (startet mit foo)
[[ "$s" =~ ^[0-9]+$ ]] # Regex-Match
Datei-Tests
[[ -e $datei ]] # existiert (egal was)
[[ -f $datei ]] # regulaere Datei
[[ -d $datei ]] # Verzeichnis
[[ -r $datei ]] # lesbar
[[ -w $datei ]] # schreibbar
[[ -x $datei ]] # ausfuehrbar
[[ -s $datei ]] # existiert und ist nicht leer
Kombinieren
[[ $a -eq 0 && $b -eq 0 ]] # und
[[ $a -eq 0 || $b -eq 0 ]] # oder
[[ ! -f $datei ]] # nicht
(( ... )) fuer Arithmetik
Bei reinen Zahlen ist (( ... )) oft schoener:
if (( a > b )); then
echo "groesser"
fi
if (( (x + y) * 2 == 10 )); then
echo "stimmt"
fi
Innerhalb der Doppelklammern brauchst du kein $ vor Variablen.
Ternaerer Operator - den gibtโs nicht
Bash hat keinen direkten Ternaer. Stattdessen:
status=$([[ $n -ge 0 ]] && echo "positiv" || echo "negativ")
Oder in (( ... )):
(( x = a > b ? a : b ))
case - das Bash-Switch
case "$tag" in
Montag)
echo "Wochenstart"
;;
Dienstag|Mittwoch|Donnerstag)
echo "Mitte der Woche"
;;
Freitag)
echo "Bald Wochenende"
;;
*)
echo "Wochenende"
;;
esac
;;schliesst einen Fall ab|erlaubt mehrere Patterns pro Case*am Ende ist der Defaultesac(case rueckwaerts) schliesst
Patterns mit Globs
case "$datei" in
*.jpg|*.png) echo "Bild" ;;
*.mp3|*.ogg) echo "Audio" ;;
*.txt) echo "Text" ;;
*) echo "Unbekannt" ;;
esac
;& und ;;& fuer Fall-Through
case $n in
1) echo "Eins" ;&
2) echo "Zwei oder Eins" ;;
esac
;& - fall through zum naechsten Block, ;;& - setze matching fort.
while
n=10
while [[ $n -gt 0 ]]; do
echo $n
(( n-- ))
done
Zeilenweise lesen
while IFS= read -r zeile; do
echo "Zeile: $zeile"
done < datei.txt
IFS=- Leerzeichen nicht als Trennzeichenread -r- Backslashes nicht interpretieren< datei.txt- Input von Datei
Das ist das Standard-Muster zum Zeilenweisen Verarbeiten.
until - das inverse while
until [[ -f /tmp/fertig ]]; do
echo "Warte..."
sleep 1
done
Laeuft bis die Bedingung wahr wird.
for
Klassisch C-artig
for (( i = 0; i < 5; i++ )); do
echo "$i"
done
Ueber Liste
for farbe in rot gruen blau; do
echo "$farbe"
done
for datei in *.txt; do
echo "verarbeite $datei"
done
Ueber Array
namen=("Max" "Anna" "Leo")
for name in "${namen[@]}"; do
echo "Hallo, $name!"
done
Range
for i in {1..5}; do
echo "$i"
done
for i in {10..1..2}; do # mit Schrittweite
echo "$i"
done
Die {1..5}-Syntax ist Brace-Expansion - sehr nuetzlich.
break und continue
for i in {1..10}; do
if (( i % 2 == 0 )); then
continue
fi
if (( i > 7 )); then
break
fi
echo "$i" # 1, 3, 5, 7
done
break N / continue N brechen/skippen N Schleifen tief.
return und exit
meine_funktion() {
local x="$1"
if [[ -z "$x" ]]; then
return 1 # verlaesst nur die Funktion
fi
echo "OK"
return 0
}
if ! meine_funktion; then
exit 1 # verlaesst das Skript
fi
Praktisches Beispiel
#!/usr/bin/env bash
set -euo pipefail
# Log-Dateien in /tmp aufraeumen - nur die aelter als 7 Tage
AUFRAEUMEN_TAGE=7
ORDNER="${1:-/tmp}"
if [[ ! -d "$ORDNER" ]]; then
echo "Ordner nicht gefunden: $ORDNER" >&2
exit 1
fi
geloescht=0
for datei in "$ORDNER"/*.log; do
[[ -f "$datei" ]] || continue
# Alter in Tagen berechnen
alter_sek=$(( $(date +%s) - $(stat -c %Y "$datei" 2>/dev/null || echo 0) ))
alter_tage=$(( alter_sek / 86400 ))
if (( alter_tage > AUFRAEUMEN_TAGE )); then
echo "loesche $datei ($alter_tage Tage alt)"
rm "$datei"
(( geloescht++ ))
fi
done
echo "Insgesamt geloescht: $geloescht"
Was ist neu?
- Typische Muster fuer Datei-Iteration (inkl. Pruefung
[[ -f ... ]]) - Kommando-Substitution
$(date +%s)kombiniert - Arithmetik in
(( ... ))
Zusammenfassung
if [[ ... ]]; then ... elif ... else ... fi- immer[[ ]]-eq,-lt, โฆ fuer Zahlen,=/!=fuer Strings[[ -f ... ]],[[ -d ... ]]fuer Datei-Tests(( ... ))fuer Arithmetik-Bedingungencase ... esacmit;;,|fuer Mehrfach-Patternswhile,until,forin verschiedenen Formencontinue/breakmit optionalem Level
Im naechsten Kapitel: Funktionen in Bash.