linux-cli-shell automation bash scripting modularity functions advanced

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>