Modular Bash: Functions & Libraries (Artikel 247)
Entwicklung modularer Bash-Skripte durch Funktionen und Libraries. Erfahren Sie alles über Sourcing, Scoping und den Aufbau wiederverwendbarer Automatisierungskomponenten.
# Modular Bash: Funktionen als Bausteine der Automatisierung
TL;DR / Management Summary Ein 1000-Zeilen-Skript ist unwartbar. Senior Admins nutzen Modularisierung. Wir teilen Logik in kleine, testbare Funktionen auf und lagern diese in Library-Dateien (
lib.sh) aus. Diese Bibliotheken werden in Hauptskripte eingebunden (Sourcing). Dies ermöglicht es, Standard-Aufgaben wie “Logging”, “Fehlerprüfung” oder “Datenbank-Connects” einmal zu schreiben und in hunderten Skripten wiederzuverwenden.
# 1. Einführung & Architektur
Das Baukasten-Prinzip.
Anstatt Logik in jedem Skript neu zu implementieren, laden wir Funktionen aus einer Bibliothek.
# Die modulare Struktur (Mermaid)
graph TD
A[Main Script: deploy.sh] --> B[Source: lib/common.sh]
A --> C[Source: lib/network.sh]
B --> D[Function: log_info]
B --> E[Function: check_root]
C --> F[Function: get_ip_addr]
subgraph "The Library"
B
C
end
# 2. Erstellung einer Library (lib.sh)
Standard-Tools bündeln.
Datei: utils.sh
# WICHTIG: Kein Shebang nötig, da die Datei nur gesourced wird
log() {
local level="$1"
shift
echo "[$(date +'%Y-%m-%dT%H:%M:%S')] [${level}] $*"
}
check_root() {
if [[ $EUID -ne 0 ]]; then
log "ERROR" "This script must be run as root"
return 1
fi
}
# 3. Sourcing im Hauptskript
Die Module laden.
Nutzen Sie den Punkt-Operator (.) oder source.
#!/bin/bash
# Ermittle den Pfad des Skripts für relatives Sourcing
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Module laden
source "${SCRIPT_DIR}/lib/utils.sh"
# Nutzung
check_root || exit 1
log "INFO" "Starting deployment..."
# 4. Day-2 Operations: Scoping & Return Values
Datenfluss kontrollieren.
# Lokale Variablen (Essenziell!)
Nutzen Sie immer local, um zu verhindern, dass Funktionen globale Variablen überschreiben.
calculate_sum() {
local a=$1
local b=$2
local res=$((a + b))
echo "$res" # Rückgabe via stdout
}
# Rückgabewerte (Status Codes)
Funktionen geben nur Status-Codes (0-255) via return zurück. Für Daten nutzen wir echo.
# Aufruf
result=$(calculate_sum 5 10)
status=$?
# 5. Troubleshooting & “War Stories”
Wenn die Module sich verstecken.
# Story 1: “Der falsche relative Pfad”
Symptom: Ein Skript läuft manuell aufgerufen perfekt, schlägt aber via Cron fehl mit: source: utils.sh: file not found.
Ursache: source utils.sh sucht im aktuellen Arbeitsverzeichnis (CWD). Bei Cron ist das meist $HOME.
Lösung: Nutzen Sie immer absolute Pfade oder berechnen Sie den Pfad relativ zum Skript-Ort (siehe Beispiel unter Punkt 3).
# Story 2: “Variablen-Kollision im Team”
Symptom: Ein Skript verhält sich seltsam, Variablen ändern sich “magisch”.
Ursache: Zwei verschiedene Library-Files nutzen die gleiche globale Variable (z.B. TMP_DIR).
Lösung: Nutzen Sie Namensräume (Namespacing). Benennen Sie Variablen in Libraries mit einem Präfix: LIB_UTILS_TMP_DIR.
# 6. Fazit & Empfehlung
- Trockenheit: Folgen Sie dem DRY (Don’t Repeat Yourself) Prinzip.
- Dokumentation: Jede Funktion in einer Library sollte einen kurzen Header haben (Zweck, Parameter, Rückgabe).
- Testing: Testen Sie Ihre Libraries separat mit kleinen Test-Skripten.
# Anhang: Cheatsheet
| Aufgabe | Befehl / Syntax |
|---|---|
| Datei einbinden | source <file> oder . <file> |
| Lokale Variable | local name="value" |
| Konstante | readonly NAME="value" |
| Funktion beenden | return <0-255> |
| Skript-Verzeichnis | $(dirname $(readlink -f $0)) |
| Export für Sub-Prozesse | export VAR |
| Prüfen ob Funktion existiert | declare -F <name> |