MWCodebymw.de ↗
Terminal

timeout: Befehle, die sich festfahren, nach N Sekunden abbrechen

Ein Skript, das jede Nacht läuft, hängt irgendwann an einem Server, der nicht mehr antwortet – und dann läuft es eben nicht mehr zu Ende. timeout ist ein einziges Wort vor dem Befehl und macht aus dem Hänger einen sauberen Fehler.

Der unangenehmste Fehler in einem Automatisierungsskript ist nicht der Absturz. Es ist der Stillstand: curl wartet auf einen Server, der die Verbindung offen hält und nichts sendet. Das Skript wartet mit. Am nächsten Morgen ist die Sicherung nicht gelaufen, es gibt keine Fehlermeldung, und im Prozessbaum steht ein Aufruf von vor zehn Stunden.

Dagegen gibt es ein Wort, das man einfach davorschreibt.

Der Normalfall

timeout 30 curl -fsSL https://api.example.com/status

Läuft der Befehl länger als 30 Sekunden, bekommt er SIGTERM und timeout beendet sich mit Status 124. Genau darauf kannst du reagieren:

#!/usr/bin/env bash
set -euo pipefail

if timeout 30 curl -fsSL -o /tmp/status.json https://api.example.com/status; then
    echo "Status geholt."
elif [ $? -eq 124 ]; then
    echo "API antwortet nicht – nach 30 s abgebrochen." >&2
    exit 1
else
    echo "API hat einen Fehler gemeldet." >&2
    exit 1
fi

Die Dauer nimmt Einheiten: timeout 90s, timeout 5m, timeout 2h, timeout 1d. Ohne Einheit sind es Sekunden.

Wenn der Befehl sich nicht beenden lässt

SIGTERM ist eine Bitte, kein Befehl. Manche Programme fangen das Signal ab und räumen erst auf – oder ignorieren es. Dafür gibt es -k:

timeout -k 10 60 ./import.sh

Nach 60 Sekunden kommt SIGTERM. Ist der Prozess 10 Sekunden später immer noch da, folgt SIGKILL, und das kann niemand ignorieren. Ich setze -k bei allem, was auf fremdem Code aufsetzt.

Braucht dein Programm ein anderes Signal, sag es dazu:

timeout --signal=INT 30 python3 lange_rechnung.py

Im Cron ist es Pflicht

0 3 * * * timeout -k 30 20m /usr/local/bin/backup.sh

Ohne Grenze bleibt ein hängender nächtlicher Lauf einfach stehen – und der Lauf am Tag darauf startet daneben. Nach einer Woche hast du sieben blockierte Prozesse und immer noch keine Sicherung. Mit timeout bricht der Lauf ab, der nächste beginnt sauber, und im Log steht eine Zeile, die man lesen kann.

Drei Dinge, die du wissen solltest

Status 124 heißt „Zeit abgelaufen", 125 heißt „timeout selbst hat versagt", 126 „Befehl nicht ausführbar", 127 „Befehl nicht gefunden". Wurde der Befehl durch ein Signal beendet, ist es 128 + Signalnummer. Diese Zahlen zu kennen ist der halbe Wert des Werkzeugs – nur so kannst du „hing" von „ist fehlgeschlagen" unterscheiden.

Es wirkt auf den gestarteten Prozess, nicht automatisch auf dessen Kinder. Startet dein Skript selbst weitere Programme, können die weiterlaufen. timeout --foreground und in hartnäckigen Fällen eine eigene Prozessgruppe helfen; im Alltag reicht meistens, timeout direkt vor den wirklich langsamen Befehl zu setzen statt vor das ganze Skript.

Es ersetzt keine Timeouts des Werkzeugs selbst. curl --max-time 30 ist präziser als timeout 30 curl, weil curl weiß, ob gerade Daten fließen. timeout ist das Netz darunter für alles, was so etwas nicht anbietet – und das sind die meisten Programme.

timeout gehört zu den GNU coreutils und ist auf jedem Linux dabei. Auf macOS heißt es gtimeout (aus coreutils via Homebrew).

Seit ich das konsequent vor jeden Netzaufruf in meinen Deploy- und Sicherungsskripten setze, gibt es keine stehengebliebenen Läufe mehr – nur noch Fehlermeldungen, und die kann man beheben. Wenn du solche Skripte für dein Projekt gebrauchen kannst, meld dich gern über bymw.de.

Quellen

#timeout#Cron#Zuverlässigkeit#Shell#Automatisierung

Du brauchst mehr als ein Snippet?

Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.

Projekt anfragen →