Shell-Scripting: Bash-Automatisierung für Server
Lerne Shell-Scripting mit Bash: Von Grundlagen über Cron-Jobs bis zur automatisierten Serverüberwachung. Praxis-Guide mit sofort einsetzbaren Skripten.
Wer Linux-Server administriert, kommt an einem Thema nicht vorbei: Shell-Scripting mit Bash. Wiederkehrende Aufgaben wie Log-Rotation, Backups oder Systemüberwachung manuell auszuführen, kostet Zeit und ist fehleranfällig. Mit wenigen Zeilen Bash-Code lassen sich diese Prozesse vollständig automatisieren – zuverlässig, transparent und ohne zusätzliche Software. Dieser Praxis-Guide führt dich von den Grundlagen bis zu produktionsreifen Skripten, die du sofort auf deinen Servern einsetzen kannst.
Bash-Grundlagen: Das Fundament für Shell-Scripting
Jedes Bash-Skript beginnt mit dem sogenannten Shebang – einer Zeile, die dem System mitteilt, welcher Interpreter verwendet werden soll. Danach folgt die eigentliche Logik.
#!/usr/bin/env bash
set -euo pipefail
# Variablen definieren
SERVER_NAME=$(hostname)
DATUM=$(date +"%Y-%m-%d_%H-%M-%S")
LOG_DIR="/var/log/custom"
echo "Skript gestartet auf ${SERVER_NAME} am ${DATUM}"
Die Zeile set -euo pipefail ist in der Praxis unverzichtbar. Sie sorgt dafür, dass das Skript bei Fehlern sofort abbricht (-e), undefinierte Variablen als Fehler behandelt (-u) und auch Fehler in Pipes erkennt (pipefail). Ohne diese Absicherung laufen fehlerhafte Skripte stillschweigend weiter und richten im schlimmsten Fall Schaden an.
Variablen, Bedingungen und Schleifen
Die drei Bausteine, die du für Linux-Automatisierung am häufigsten brauchst:
#!/usr/bin/env bash
set -euo pipefail
# Bedingung: Prüfen, ob ein Verzeichnis existiert
BACKUP_DIR="/backup/daily"
if [[ ! -d "${BACKUP_DIR}" ]]; then
mkdir -p "${BACKUP_DIR}"
echo "Backup-Verzeichnis erstellt: ${BACKUP_DIR}"
fi
# Schleife: Alle .conf-Dateien verarbeiten
for config in /etc/nginx/sites-enabled/*.conf; do
echo "Prüfe Konfiguration: ${config}"
nginx -t -c "${config}" 2>/dev/null || echo "FEHLER in ${config}"
done
# Funktionen für wiederverwendbare Logik
log_message() {
local level="$1"
local message="$2"
echo "[$(date +"%Y-%m-%d %H:%M:%S")] [${level}] ${message}" >> /var/log/custom/automation.log
}
log_message "INFO" "Skript erfolgreich durchgelaufen"
Praxis-Skript: Automatisiertes Server-Backup
Ein vollständiges Backup-Skript gehört zur Grundausstattung jeder Serveradministration. Das folgende Beispiel sichert eine MySQL-Datenbank und wichtige Konfigurationsdateien, komprimiert alles und löscht alte Backups automatisch.
#!/usr/bin/env bash
set -euo pipefail
# === Konfiguration ===
BACKUP_BASE="/backup"
RETENTION_DAYS=14
DB_NAME="production_db"
DB_USER="backup_user"
DB_PASS_FILE="/root/.db_backup_pw"
DATUM=$(date +"%Y-%m-%d_%H-%M-%S")
BACKUP_DIR="${BACKUP_BASE}/${DATUM}"
LOG_FILE="/var/log/backup.log"
# === Funktionen ===
log() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" | tee -a "${LOG_FILE}"
}
cleanup() {
if [[ $? -ne 0 ]]; then
log "FEHLER: Backup fehlgeschlagen!"
# Optional: Benachrichtigung senden
curl -s -X POST "https://hooks.slack.com/services/DEIN/WEBHOOK/URL" \
-d "{\"text\":\"⚠️ Backup fehlgeschlagen auf $(hostname)\"}" || true
fi
}
trap cleanup EXIT
# === Hauptlogik ===
log "Starte Backup..."
mkdir -p "${BACKUP_DIR}"
# Datenbank sichern
log "Sichere Datenbank ${DB_NAME}..."
mysqldump --user="${DB_USER}" \
--password="$(cat "${DB_PASS_FILE}")" \
--single-transaction \
--routines \
"${DB_NAME}" | gzip > "${BACKUP_DIR}/db_${DB_NAME}.sql.gz"
# Konfigurationsdateien sichern
log "Sichere Konfigurationsdateien..."
tar -czf "${BACKUP_DIR}/configs.tar.gz" \
/etc/nginx/ \
/etc/letsencrypt/ \
/etc/crontab \
2>/dev/null
# Alte Backups entfernen
log "Entferne Backups älter als ${RETENTION_DAYS} Tage..."
find "${BACKUP_BASE}" -maxdepth 1 -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} +
# Abschlussbericht
BACKUP_SIZE=$(du -sh "${BACKUP_DIR}" | cut -f1)
log "Backup abgeschlossen. Größe: ${BACKUP_SIZE}"
Besonders wichtig ist hier die trap-Anweisung: Sie fängt das Exit-Signal ab und führt die Cleanup-Funktion aus – egal ob das Skript erfolgreich war oder nicht. So wirst du bei Fehlern sofort benachrichtigt.
Cron-Jobs: Skripte zeitgesteuert ausführen
Ein Skript ist nur so nützlich wie seine Ausführung. Cron-Jobs sind der Standard-Mechanismus unter Linux, um Shell-Skripte zeitgesteuert zu starten.
# Crontab bearbeiten
crontab -e
# Backup täglich um 3:00 Uhr nachts
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1
# Serverüberwachung alle 5 Minuten
*/5 * * * * /usr/local/bin/monitor.sh >> /var/log/monitor-cron.log 2>&1
# Log-Rotation wöchentlich sonntags um 0:00
0 0 * * 0 /usr/local/bin/rotate-logs.sh >> /var/log/rotation-cron.log 2>&1
# SSL-Zertifikate monatlich erneuern
0 4 1 * * certbot renew --quiet && systemctl reload nginx
Ein häufiger Fehler: Cron verwendet eine eingeschränkte Umgebung ohne die gewohnten PATH-Variablen. Definiere Pfade in deinen Skripten daher immer absolut, oder setze die PATH-Variable am Anfang der Crontab:
# Am Anfang der Crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
Serverüberwachung mit Bash automatisieren
Das folgende Monitoring-Skript prüft die wichtigsten Systemressourcen und warnt dich, bevor Probleme entstehen. Ein klassischer Anwendungsfall für Shell-Scripting in der täglichen Administration.
#!/usr/bin/env bash
set -euo pipefail
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname -f)
ALERTS=""
# CPU-Last prüfen (Durchschnitt der letzten 5 Minuten)
LOAD=$(awk '{print $2}' /proc/loadavg)
CPU_CORES=$(nproc)
LOAD_THRESHOLD=$(echo "${CPU_CORES} * 0.8" | bc)
if (( $(echo "${LOAD} > ${LOAD_THRESHOLD}" | bc -l) )); then
ALERTS+="⚠️ CPU-Last hoch: ${LOAD} (Schwelle: ${LOAD_THRESHOLD})\n"
fi
# Festplattenauslastung prüfen
while read -r usage mount; do
usage_num=${usage%\%}
if [[ ${usage_num} -gt 85 ]]; then
ALERTS+="⚠️ Festplatte ${mount}: ${usage} belegt\n"
fi
done < <(df -h --output=pcent,target | tail -n +2 | grep -v tmpfs)
# RAM-Auslastung prüfen
MEM_TOTAL=$(free -m | awk '/Mem:/ {print $2}')
MEM_AVAILABLE=$(free -m | awk '/Mem:/ {print $7}')
MEM_USAGE_PCT=$(( (MEM_TOTAL - MEM_AVAILABLE) * 100 / MEM_TOTAL ))
if [[ ${MEM_USAGE_PCT} -gt 90 ]]; then
ALERTS+="⚠️ RAM-Auslastung: ${MEM_USAGE_PCT}%\n"
fi
# Wichtige Dienste prüfen
for service in nginx mysql ssh; do
if ! systemctl is-active --quiet "${service}" 2>/dev/null; then
ALERTS+="🔴 Dienst ${service} ist nicht aktiv!\n"
# Automatischer Neustart-Versuch
systemctl restart "${service}" 2>/dev/null && \
ALERTS+="✅ ${service} wurde automatisch neu gestartet\n"
fi
done
# Alerts versenden
if [[ -n "${ALERTS}" ]]; then
echo -e "Server-Alert: ${HOSTNAME}\n\n${ALERTS}" | \
mail -s "⚠️ Server-Alert: ${HOSTNAME}" "${ALERT_EMAIL}"
fi
Dieses Skript kombiniert mehrere Überwachungsaspekte in einer Datei. In Verbindung mit dem oben eingerichteten Cron-Job (alle 5 Minuten) erhältst du ein leichtgewichtiges Monitoring, das ohne externe Tools auskommt.
Log-Analyse als Ergänzung
Neben System-Metriken lassen sich auch Logdateien automatisiert auswerten. Das folgende Snippet erkennt auffällige Muster in Nginx-Access-Logs:
#!/usr/bin/env bash
set -euo pipefail
ACCESS_LOG="/var/log/nginx/access.log"
THRESHOLD=500
# IP-Adressen mit mehr als 500 Requests in der letzten Stunde
EINE_STUNDE_ALT=$(date -d '1 hour ago' +"%d/%b/%Y:%H")
awk -v zeitstempel="${EINE_STUNDE_ALT}" '$0 ~ zeitstempel {print $1}' "${ACCESS_LOG}" \
| sort | uniq -c | sort -rn \
| while read -r count ip; do
if [[ ${count} -gt ${THRESHOLD} ]]; then
echo "Verdächtige IP: ${ip} mit ${count} Requests"
# Optional: IP automatisch sperren
# ufw deny from "${ip}" comment "Auto-Block: ${count} Requests/h"
fi
done
Best Practices für produktionsreifes Shell-Scripting
Bevor du deine Skripte auf produktive Server ausrollst, beachte diese bewährten Praktiken:
- Fehlerbehandlung ist Pflicht: Nutze immer
set -euo pipefailundtrapfür Aufräumarbeiten. - Logging statt Echo: Schreibe strukturierte Logs mit Zeitstempel und Log-Level in dedizierte Dateien.
- Passwörter niemals hartcodiert: Verwende separate Dateien mit restriktiven Berechtigungen (
chmod 600) oder Umgebungsvariablen. - Shellcheck nutzen: Das Tool
shellcheckfindet häufige Fehler statisch, bevor sie in Produktion Schaden anrichten. - Idempotenz sicherstellen: Ein Skript sollte bei mehrfacher Ausführung dasselbe Ergebnis liefern. Prüfe vor jeder Aktion, ob sie nötig ist.
- Testen in Staging: Führe neue Skripte zuerst mit
bash -x skript.shim Debug-Modus aus, um jeden Schritt nachzuvollziehen.
# Shellcheck installieren und verwenden
apt install shellcheck
shellcheck /usr/local/bin/backup.sh
# Debug-Modus für Fehlersuche
bash -x /usr/local/bin/monitor.sh
Fazit
Shell-Scripting mit Bash ist kein Relikt aus vergangenen Zeiten, sondern ein unverzichtbares Werkzeug für die moderne Serveradministration. Die Kombination aus einfacher Syntax, direktem Systemzugriff und universeller Verfügbarkeit auf praktisch jedem Linux-System macht Bash zur ersten Wahl für Automatisierungsaufgaben. Mit den vorgestellten Skripten für Backup, Monitoring und Log-Analyse hast du eine solide Grundlage, die du an deine Infrastruktur anpassen kannst. Starte mit einem einzelnen Skript, das dir täglich Zeit spart, und baue deine Linux-Automatisierung Schritt für Schritt aus. Der Aufwand für ein gut geschriebenes Bash-Skript zahlt sich bereits nach der zweiten automatischen Ausführung aus.