linux-ubuntu-debian automation bash scripting shell advanced

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.sh aus.
  • 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.