Zum Inhalt springen
Bash Anfรคnger 25 min

if, case & Schleifen

Kontrollstrukturen in Bash: if mit Tests, case fuer Pattern Matching, for, while und typische Fallstricke bei den Vergleichs-Operatoren.

Aktualisiert:
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 Zeile
  • elif (ein Wort!)
  • fi schliesst (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 Default
  • esac (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 Trennzeichen
  • read -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-Bedingungen
  • case ... esac mit ;;, | fuer Mehrfach-Patterns
  • while, until, for in verschiedenen Formen
  • continue / break mit optionalem Level

Im naechsten Kapitel: Funktionen in Bash.

Zurรผck zum Bash Kurs