Verwendung des `sleep`-Befehls in Bash-Skripten unter Linux
Der `sleep`-Befehl in Linux unterbricht die Skriptausführung für eine genau definierte Dauer — angegeben in Sekunden, Minuten, Stunden oder Tagen — unter Verwendung der Syntax `sleep [NUMBER][SUFFIX]`. Er ist eines der betrieblich wichtigsten Primitiven beim Bash-Scripting und ermöglicht Rate Limiting, Retry-Logik, Prozesssynchronisierung und zeitgesteuerte Automatisierung ohne externe Scheduler.
Im Gegensatz zu cron oder `at` arbeitet `sleep` vollständig im eigenen Prozesskontext des Skripts, was es zum richtigen Werkzeug macht, wenn die Verzögerung relativ zur Fertigstellung eines vorherigen Befehls sein muss und nicht einer absoluten Wanduhrzeit.
Syntax und Zeiteinheiten-Referenz
“`bash
sleep NUMBER[SUFFIX]
“`
| Suffix | Einheit | Beispiel | Äquivalent in Sekunden |
|---|
| ——– | ——— | —————- | ———————– |
|---|
| `s` | Sekunden | `sleep 30s` | 30 |
|---|
| `m` | Minuten | `sleep 5m` | 300 |
|---|
| `h` | Stunden | `sleep 2h` | 7200 |
|---|
| `d` | Tage | `sleep 1d` | 86400 |
|---|
| (keines) | Sekunden | `sleep 10` | 10 |
|---|
Das Suffix ist optional. Wenn es weggelassen wird, ist die Standardeinheit Sekunden. Auf GNU/Linux-Systemen (GNU coreutils) akzeptiert `sleep` auch Gleitkommazahlen und mehrere Argumente — eine Funktion, die bei BSD- und macOS-Implementierungen fehlt, sofern GNU coreutils nicht über Homebrew installiert ist.
“`bash
GNU coreutils: both of these are valid
sleep 1.5
sleep 1m 30s # Equivalent to 90 seconds
“`
Wichtiger Hinweis zur Portabilität: POSIX schreibt nur ganzzahlige Sekunden ohne Suffix vor. Wenn Ihr Skript auf Alpine Linux (BusyBox), macOS oder AIX laufen muss, beschränken Sie sich auf `sleep INTEGER` und vermeiden Sie die Verkettung mehrerer Argumente.
Hauptanwendungsfälle in Bash-Skripten
1. Sequenzielle Verzögerung zwischen Befehlen
Die einfachste Anwendung — das Einfügen einer Pause zwischen zwei Operationen, bei denen der zweite Befehl erst beginnen darf, wenn eine reale Bedingung Zeit hatte, sich zu stabilisieren:
“`bash
#!/bin/bash
echo "Restarting nginx…"
systemctl restart nginx
sleep 3
systemctl status nginx
“`
Die 3-Sekunden-Pause berücksichtigt hier die asynchrone Startsequenz des Service-Managers. Ohne sie kann `status` einen veralteten Zustand melden, der erfasst wurde, bevor der Prozess vollständig initialisiert ist.
2. Polling-Schleife mit exponentiellem Backoff
Eine naive Retry-Schleife mit festem Intervall verschwendet Ressourcen und kann die Last auf einem überlasteten nachgelagerten Dienst verstärken. Das korrekte Muster ist exponentieller Backoff mit Jitter:
“`bash
#!/bin/bash
MAX_RETRIES=6
DELAY=2
for (( attempt=1; attempt<=MAX_RETRIES; attempt++ )); do
if curl -sf https://api.example.com/health > /dev/null; then
echo "Service healthy on attempt $attempt."
exit 0
fi
echo "Attempt $attempt failed. Retrying in ${DELAY}s…"
sleep "$DELAY"
DELAY=$(( DELAY * 2 ))
done
echo "Service unreachable after $MAX_RETRIES attempts." >&2
exit 1
“`
Dies verdoppelt die Wartezeit bei jedem Fehlschlag: 2s, 4s, 8s, 16s, 32s, 64s. Die maximale Gesamtwartezeit vor dem Aufgeben beträgt 126 Sekunden. Dieses Muster ist Standard in Produktions-Deployment-Skripten, Health Checks und CI/CD-Pipelines.
3. Ratenbegrenzte API-Aufrufe
Bei der Interaktion mit APIs, die Anfragekontingente durchsetzen, erzwingt `sleep` das erforderliche Intervall zwischen Anfragen:
“`bash
#!/bin/bash
API_KEY="your_key_here"
ENDPOINTS=("users" "orders" "products" "inventory")
for endpoint in "${ENDPOINTS[@]}"; do
curl -s -H "Authorization: Bearer $API_KEY"
"https://api.example.com/v1/${endpoint}"
-o "${endpoint}.json"
echo "Fetched: $endpoint"
sleep 1 # Respect 1 req/sec rate limit
done
“`
4. Zeitgesteuerte Ausführung von Hintergrundaufgaben
Das Ausführen eines verzögerten Befehls ohne Blockierung der aktuellen Shell-Sitzung erfordert die Kombination von `sleep` mit Subshell-Hintergrundausführung:
“`bash
Trigger a cache flush 60 seconds after deployment completes
( sleep 60 && redis-cli FLUSHDB ) &
echo "Cache flush scheduled. PID: $!"
“`
Die Variable `$!` erfasst die PID der Hintergrund-Subshell, die Sie später mit `wait` oder `kill` verwenden können, wenn die Aufgabe abgebrochen werden muss.
5. Watchdog- und Prozessüberwachungsschleife
“`bash
#!/bin/bash
SERVICE="mysqld"
CHECK_INTERVAL=30
while true; do
if ! pgrep -x "$SERVICE" > /dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') $SERVICE not running. Restarting…"
>> /var/log/watchdog.log
systemctl start "$SERVICE"
fi
sleep "$CHECK_INTERVAL"
done
“`
Dieses Muster wird bei der einfachen Prozessüberwachung verwendet, wenn ein vollständiger Supervisor-Daemon (systemd, supervisord, s6) nicht verfügbar oder ungeeignet ist — häufig in containerisierten Umgebungen oder minimalen VPS Hosting-Instanzen.
6. Countdown-Timer mit Benutzerrückmeldung
Für interaktive Skripte, bei denen der Operator die verbleibende Wartezeit sehen muss:
“`bash
#!/bin/bash
COUNTDOWN=10
echo "Starting in:"
for (( i=COUNTDOWN; i>=1; i– )); do
printf "r%2d seconds remaining…" "$i"
sleep 1
done
printf "rGo! n"
“`
`printf "r"` überschreibt die aktuelle Zeile, anstatt neue Zeilen anzuhängen, und erzeugt so einen sauberen Terminal-Countdown.
`sleep` vs. alternative Timing-Mechanismen
| Mechanismus | Granularität | Blockiert Shell | Absolute Zeit | Anwendungsfall |
|---|
| —————– | —————– | ————– | ————— | ———————————————– |
|---|
| `sleep` | Unter einer Sekunde (GNU) | Ja (außer `&`) | Nein | Relative Verzögerungen innerhalb von Skripten |
|---|
| `cron` | 1 Minute | Nein | Ja | Wiederkehrende geplante Jobs |
|---|
| `at` | 1 Minute | Nein | Ja | Einmalige zukünftige Ausführung |
|---|
| `systemd timer` | 1 Sekunde | Nein | Ja | Persistente, protokollierte, abhängigkeitsbewusste Jobs |
|---|
| `usleep` (C) | Mikrosekunde | Ja | Nein | Kernel/C-Ebene Präzision (nicht Bash-nativ) |
|---|
| `read -t` | Unter einer Sekunde | Ja | Nein | Timeout mit optionaler Benutzereingabe |
|---|
Wann `read -t` statt `sleep` verwendet werden sollte: Wenn Ihr Skript pausieren muss, aber auch eine Unterbrechung oder Reaktion des Benutzers während der Wartezeit ermöglichen soll, ist `read -t SECONDS` das richtige Primitiv. Es gibt bei Timeout den Exit-Code 1 zurück und bei Drücken der Eingabetaste 0, was Ihnen bedingte Logik ohne einen separaten Prozess ermöglicht.
“`bash
echo "Press Enter to skip the 10-second wait, or wait for automatic continuation."
read -t 10 -r || true
echo "Continuing…"
“`
Präzision, Gleitkommazahlen und Plattformverhalten
GNU `sleep` akzeptiert Dezimalbrüche, was bei Skripten wichtig ist, die Animationen steuern, das Log-Tailing drosseln oder Echtzeit-Datenfeeds simulieren:
“`bash
Tail a log file and print one line per 0.2 seconds (5 lines/sec)
while IFS= read -r line; do
echo "$line"
sleep 0.2
done < /var/log/app.log
“`
Die tatsächliche Sleep-Dauer ist ein Minimum, keine Garantie. Der Kernel-Scheduler kann den Prozess je nach Systemlast und Timer-Auflösung (`CONFIG_HZ`) leicht verspätet aufwecken. Auf einem stark ausgelasteten Dedicated Server, der Dutzende gleichzeitiger Prozesse ausführt, kann ein `sleep 0.1` tatsächlich 0,11–0,15 Sekunden pausieren. Für Skripte, bei denen diese Abweichung nicht akzeptabel ist, verwenden Sie eine monotone Uhrreferenz:
“`bash
#!/bin/bash
INTERVAL=5
NEXT=$(date +%s%N) # Current time in nanoseconds
while true; do
NEXT=$(( NEXT + INTERVAL * 1000000000 ))
do_work
NOW=$(date +%s%N)
REMAINING=$(( (NEXT – NOW) / 1000000 )) # Convert to milliseconds
[ "$REMAINING" -gt 0 ] && sleep "$(echo "scale=3; $REMAINING/1000" | bc)"
done
“`
Diese drift-kompensierende Schleife hält ein konsistentes Intervall aufrecht, unabhängig davon, wie lange `do_work` dauert.
Signalbehandlung und Unterbrechen von `sleep`
Ein laufender `sleep`-Prozess reagiert auf Signale. Das Senden von `SIGALRM` an den Sleep-Prozess weckt ihn sofort auf. Praktischer ist, dass das Drücken von `Ctrl+C` `SIGINT` an die gesamte Prozessgruppe sendet und sowohl das Skript als auch jeden im Vordergrund laufenden `sleep` beendet.
Um ein Skript so zu gestalten, dass es Unterbrechungen während eines Sleeps sauber behandelt:
“`bash
#!/bin/bash
cleanup() {
echo "Interrupted. Cleaning up…"
exit 1
}
trap cleanup SIGINT SIGTERM
echo "Waiting 60 seconds…"
sleep 60 &
SLEEP_PID=$!
wait "$SLEEP_PID"
echo "Wait complete."
“`
Durch das Hintergrundausführen von `sleep` und die Verwendung von `wait` wird der `trap` bei `SIGINT` sofort ausgelöst, anstatt bis nach dem Abschluss des Sleeps verzögert zu werden. Dies ist das korrekte Muster für langfristig laufende Automatisierungsskripte auf Produktionsservern.
Praktische Fallstricke und Sonderfälle
Fallstrick 1: Verwendung von `sleep` in engen Schleifen ohne Abbruchbedingung. Eine `while true; do sleep 1; done`-Schleife ohne Ausstiegspfad läuft auf unbestimmte Zeit, belegt einen Prozessplatz und häuft sich in der `ps`-Ausgabe an. Definieren Sie immer eine maximale Iterationsanzahl oder eine Sentinel-Bedingung.
Fallstrick 2: Annahme, dass `sleep` mit Subshells synchron ist. Wenn Sie eine Subshell mit `&` forken, wartet das übergeordnete Skript nicht auf den Abschluss des `sleep` der Subshell, es sei denn, Sie rufen explizit `wait` auf. Dies verursacht Race Conditions in parallelen Deployment-Skripten.
Fallstrick 3: Hardcodierte Verzögerungen für die Dienstbereitschaft. Die Verwendung von `sleep 5` nach dem Starten eines Dienstes ist fehleranfällig. Der Dienst kann in 1 Sekunde bereit sein oder unter Last 30 Sekunden benötigen. Die robuste Alternative ist ein Bereitschafts-Poll:
“`bash
#!/bin/bash
wait_for_port() {
local host="$1" port="$2" timeout="${3:-30}"
local elapsed=0
until nc -z "$host" "$port" 2>/dev/null; do
[ "$elapsed" -ge "$timeout" ] && return 1
sleep 1
(( elapsed++ ))
done
}
systemctl start postgresql
wait_for_port localhost 5432 30 && echo "PostgreSQL ready."
“`
Fallstrick 4: Gleitkomma-Sleep auf BusyBox-Systemen. Alpine Linux-Container verwenden BusyBox’s `sleep`, das keine Dezimalzahlen unterstützt. Der Versuch `sleep 0.5` wird einen Fehler auslösen. Überprüfen Sie Ihre Umgebung, bevor Sie Skripte deployen, die auf Sub-Sekunden-Präzision angewiesen sind.
Integration von `sleep` in automatisierte Server-Workflows
Auf einem verwalteten VPS mit cPanel kombinieren automatisierte Wartungsskripte häufig `sleep` mit cron, um Sub-Minuten-Scheduling zu implementieren. Da die minimale Auflösung von cron eine Minute beträgt, können Sie 15-Sekunden-Intervalle wie folgt erreichen:
“`bash
crontab entry — runs the script 4 times per minute
- * * * * /usr/local/bin/check_queue.sh
- * * * * sleep 15 && /usr/local/bin/check_queue.sh
- * * * * sleep 30 && /usr/local/bin/check_queue.sh
- * * * * sleep 45 && /usr/local/bin/check_queue.sh
“`
Diese Technik wird häufig für Queue-Prozessoren, Health Checks und Metrik-Collector auf gemeinsam genutzter Infrastruktur verwendet, wo die Installation eines dedizierten Job-Schedulers nicht erlaubt ist.
Für SSL-Zertifikat-Erneuerungsskripte bietet `sleep` die Verzögerung zwischen Versuchen, wenn die ACME-Challenge-Propagierung erfordert, dass der DNS-TTL abläuft, bevor die CA die Eigentümerschaft verifizieren kann. Wenn Sie Zertifikate auf Ihrer eigenen Infrastruktur verwalten, profitieren SSL-Zertifikate mit automatisierten Erneuerungspipelines von präzise abgestimmten Retry-Intervallen.
Ebenso verwenden Domain-Propagierungs-Verifizierungsskripte — nützlich nach der Aktualisierung von Einträgen über die Domain-Registrierung — `sleep`-Schleifen, um DNS-Resolver in Intervallen abzufragen, die auf erwartete TTL-Werte abgestimmt sind.
Entscheidungsmatrix: Die richtige Verzögerungsstrategie wählen
| Szenario | Empfohlener Ansatz |
|---|
| ———————————————– | ————————————————— |
|---|
| Feste Pause zwischen zwei sequenziellen Befehlen | `sleep N` |
|---|
| Retry bis Erfolg, Thundering Herd vermeiden | Exponentieller Backoff mit `sleep` |
|---|
| Wiederkehrende Aufgabe alle N Minuten | `cron` (nicht `sleep`) |
|---|
| Sub-Minuten-Wiederholungsaufgabe | `cron` + `sleep`-Offset-Trick |
|---|
| Verzögerung ohne Blockierung des Terminals | `( sleep N && command ) &` |
|---|
| Pause mit Benutzer-Unterbrechungsmöglichkeit | `sleep N &` + `wait $!` + `trap` |
|---|
| Dienstbereitschaftsprüfung | Port/Health-Poll-Schleife mit `sleep 1` pro Versuch |
|---|
| Hochpräzises Intervall (drift-kompensierend) | Monotone Uhrreferenz mit berechnetem `sleep` |
|---|
| Sub-Sekunden-Verzögerung auf Alpine/BusyBox | Vermeiden; ganzzahlige Sekunden verwenden oder Basis-Image wechseln |
|---|
Wichtige technische Erkenntnisse
- Verwenden Sie `sleep "$VARIABLE"` immer mit doppelten Anführungszeichen, um Word-Splitting-Fehler zu vermeiden, wenn die Variable eine Dezimalzahl enthält.
- Bevorzugen Sie `sleep 1m 30s` gegenüber `sleep 90` für bessere Lesbarkeit in langfristig laufenden Wartungsskripten.
- Führen Sie `sleep` mit `wait` im Hintergrund aus und verwenden Sie einen `trap`, wenn Ihr Skript während der Pause auf Signale reagieren muss.
- Verwenden Sie niemals ein hardcodiertes `sleep` als Ersatz für eine ordnungsgemäße Bereitschaftsprüfung — verwenden Sie eine Polling-Schleife mit Timeout.
- Überprüfen Sie das `sleep`-Verhalten auf dem Zielbetriebssystem, bevor Sie Skripte deployen, die Gleitkomma- oder Multi-Argument-Syntax verwenden.
- Protokollieren Sie auf Produktionsservern den Zeitstempel vor und nach langen `sleep`-Aufrufen, um Scheduler-Drift bei der Nachfallanalyse zu erkennen.
- Wenn Sie Automatisierung auf VPS Control Panels aufbauen, prüfen Sie, ob der Task-Scheduler des Panels bereits eine Intervallsteuerung bietet, bevor Sie manuell `sleep`-Logik hinzufügen.
FAQ
Verbraucht `sleep` während des Wartens CPU?
Nein. `sleep` ruft `nanosleep()` (oder Äquivalent) auf Kernel-Ebene auf und versetzt den Prozess in einen unterbrechbaren Sleep-Zustand (`S` in der `ps`-Ausgabe). Es verbraucht während der Wartezeit keine CPU-Zyklen — nur eine kleine Menge Speicher für den Prozesseintrag in der Prozesstabelle.
Was ist der maximale Wert, den `sleep` akzeptiert?
Auf GNU/Linux akzeptiert `sleep` Werte bis zu den Grenzen einer `double`-Gleitkommazahl, was für praktische Zwecke effektiv unbegrenzt ist. `sleep 1d` (86400 Sekunden) ist üblich; `sleep 365d` ist gültig. Das praktische Limit ist die Systemlaufzeit.
Warum schlägt `sleep 0.5` in meinem Docker-Container fehl?
Alpine Linux verwendet BusyBox, dessen `sleep`-Implementierung nur ganzzahlige Sekunden akzeptiert. Wechseln Sie zu einem Debian- oder Ubuntu-Basis-Image, oder installieren Sie GNU coreutils (`apk add coreutils`), um Dezimalunterstützung zu aktivieren.
Kann ich einen im Hintergrund laufenden `sleep`-Prozess abbrechen?
Ja. Erfassen Sie seine PID mit `SLEEP_PID=$!` unmittelbar nach dem Hintergrundausführen, und verwenden Sie dann `kill "$SLEEP_PID"`, um ihn zu beenden. Wenn Sie `( sleep N && command ) &` verwendet haben, verhindert das Beenden der Subshell-PID auch die Ausführung des nachfolgenden Befehls.
Ist `sleep` sicher in einem `systemd`-Service-Unit-`ExecStart`-Skript zu verwenden?
Ja, aber mit Einschränkungen. Wenn die Service-Unit `TimeoutStartSec` gesetzt hat, führt ein langer `sleep` während des Starts dazu, dass systemd den Dienst als fehlgeschlagenen Start beendet. Für Verzögerungen nach dem Start verwenden Sie `ExecStartPost` mit einem Bereitschafts-Poll, oder konfigurieren Sie `Type=forking` mit ordnungsgemäßem PID-Datei-Management, anstatt sich auf `sleep` zu verlassen, um die Initialisierung zu verzögern.
