Advanced Bash Scripting: Shell Programming (Artikel 049)
Beherrschung der Bash-Programmierung für professionelle Automatisierung. Fortgeschrittene Fehlerbehandlung, Datenstrukturen und Best Practices für produktionsreife Skripte.
# Bash Deep Dive: Skripte für die Produktion härten
TL;DR / Management Summary Fast jeder SysAdmin kann ein einfaches Bash-Skript schreiben. Ein Senior Admin hingegen schreibt Skripte, die ausfallsicher, lesbar und wartbar sind. Der Unterschied liegt im Detail: Robuste Fehlerbehandlung (
set -euo pipefail), sauberes Logging und die Nutzung fortgeschrittener Features wie Arrays und Subshells. Dieses Wiki-Modul trennt den “Copy-Paste-Admin” vom professionellen Automatisierer.
# 1. Einführung & Konzepte
Der Weg zum robusten Skript.
Ein Skript sollte niemals stillschweigend scheitern. Es muss entweder erfolgreich sein oder mit einer klaren Fehlermeldung abbrechen.
# Die “Safety Header”
Jedes professionelle Bash-Skript sollte mit diesen Zeilen beginnen:
#!/bin/bash
set -e # Beendet Skript bei Fehlern sofort
set -u # Beendet Skript bei Nutzung undefinierter Variablen
set -o pipefail # Erkennt Fehler auch innerhalb von Pipes
IFS=$'\n\t' # Sicherer Feld-Separator (verhindert Probleme mit Leerzeichen)
# 2. Fortgeschrittene Datenstrukturen
Mehr als nur Text-Variablen.
# Arrays (Indizierte Listen)
SERVERS=("web01" "web02" "db01")
for SERVER in "${SERVERS[@]}"; do
echo "Pinging $SERVER..."
done
# Associative Arrays (Key-Value Maps)
Erfordert Bash 4.0+.
declare -A APP_PORTS
APP_PORTS[nginx]=80
APP_PORTS[mysql]=3306
echo "Nginx port: ${APP_PORTS[nginx]}"
# 3. Fehlerbehandlung & Traps
Sauber aufräumen.
Wenn ein Skript abbricht, müssen temporäre Dateien gelöscht oder Sperrdateien (Lockfiles) entfernt werden.
# Der trap Befehl
TMP_FILE=$(mktemp)
# Führe Befehl bei EXIT, HUP, INT oder TERM aus
trap 'rm -f "$TMP_FILE"; echo "Interrupted!"; exit 1' INT TERM
trap 'rm -f "$TMP_FILE"' EXIT
# ... Skript Logik ...
# 4. Day-2 Operations: Logging & Locking
Enterprise Standards.
# Professionelles Logging
Schreiben Sie wichtige Infos immer nach stderr und nutzen Sie Zeitstempel.
log() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*"; >&2
}
log "INFO: Starting backup process..."
# Lockfiles (Doppel-Ausführung verhindern)
Verhindern Sie, dass ein Cronjob sich selbst überholt.
LOCKFILE="/tmp/myscript.lock"
if [ -e "${LOCKFILE}" ] && kill -0 "$(cat "${LOCKFILE}")"; then
log "ERROR: Script already running with PID $(cat "${LOCKFILE}")"
exit 1
fi
echo $$ > "${LOCKFILE}"
# 5. Troubleshooting & “War Stories”
Bugs finden statt raten.
# Story 1: “Der unendliche rm -rf”
Symptom: Ein Skript löscht das gesamte Root-Verzeichnis.
Ursache: rm -rf $MY_VAR/* wurde aufgerufen, aber $MY_VAR war leer (undefiniert).
Lösung: Nutzen Sie set -u (beendet Skript bei undefinierten Variablen) und prüfen Sie Pfade immer: [[ -n "$MY_VAR" ]] && rm -rf "$MY_VAR"/*.
# Story 2: “Subshell Variablen-Verlust”
Symptom: Eine Variable wird innerhalb eines while read Loops geändert, ist danach aber wieder leer.
Ursache: Pipes (cat file | while read...) erzeugen eine Subshell. Änderungen an Variablen fließen nicht zurück in den Parent-Prozess.
Lösung: Nutzen Sie Process Substitution: while read line; do ... done < <(cat file).
# 6. Tools für Admins
- ShellCheck: Ein statischer Analyse-Tool, das hunderte von Fehlern findet. Ein absolutes Muss vor jedem Commit!
apt install shellcheck && shellcheck myscript.sh - Bash-Debug Modus: Starten Sie Skripte mit
bash -x myscript.sh, um jede ausgeführte Zeile live zu sehen.
# 7. Fazit & Empfehlung
- Lesbarkeit: Kommentieren Sie das “Warum”, nicht das “Was”.
- Modularität: Lagern Sie oft genutzte Funktionen in eine
lib.shaus. - Wahl des Tools: Wenn ein Bash-Skript mehr als 500 Zeilen hat oder komplexe Datenverarbeitung macht, wechseln Sie zu Python.
# Anhang: Cheatsheet
| Syntax | Bedeutung |
|---|---|
${VAR:-default} |
Nutzt ‘default’, wenn VAR leer ist. |
${VAR//alt/neu} |
Ersetzt alle Vorkommen von ‘alt’ durch ‘neu’. |
$(command) |
Führt Befehl aus und gibt Ergebnis zurück (Subshell). |
[[ $A == $B ]] |
Sicherer Vergleich (modernere Version von [ ]). |
exec > >(tee -a script.log) |
Leitet allen Skript-Output zusätzlich in ein Logfile um. |