- Wenn in einem Bash-Skript zur Prüfung des Status eines Webservers wiederholt Verbindungsversuche ausgeführt werden, kann das Problem auftreten, dass der Server unerwartet in einer Endlosschleife hängen bleibt
timeout, ein Werkzeug zur Lösung dieses Problems, legt eine maximale Laufzeit für einen Befehl fest und versucht bei Überschreitung durch Senden eines Signals, den Prozess zu beenden
- Auf Shell-Built-ins wie
until lässt es sich nicht direkt anwenden; das Problem kann jedoch durch Wrapping in einen bash-Prozess oder das Auslagern in ein separates Skript gelöst werden
Warten auf einen Webserver in Bash-Skripten und das Problem der Endlosschleife
- In der Praxis werden Bash-Skripte genutzt, um Webserver einzurichten und ihren Status zu prüfen
- Während der Server hochfährt, werden nachfolgende Schritte zurückgestellt; grundsätzlich funktioniert das ohne Probleme
- Wenn der Server beim Start jedoch abstürzt, gerät das Skript in eine Endlosschleife, sodass eine Lösung nötig wurde
Beispiel für die Nutzung von until und seine Grenzen
Einführung des timeout-Dienstprogramms
- Der Befehl
timeout beendet einen Prozess, indem er ein Signal (z. B. SIGTERM) sendet, wenn der angegebene Befehl nicht innerhalb der vorgegebenen Zeit abgeschlossen wird
- Beispiel: Bei
timeout 1s sleep 5 wird nach 1 Sekunde versucht, den sleep-Prozess zu beenden
- Beim Beenden wird ein Fehlercode (z. B. 124) zurückgegeben
Versuch, timeout mit until zu kombinieren, und das Problem dabei
Lösung: Wrapping in einen Bash-Prozess oder Nutzung eines externen Skripts
1 Kommentare
Hacker-News-Kommentare
Einer meiner liebsten, wenig bekannten Tricks ist die Verwendung von
straceFault Injection, um Fehler bei verschiedenen System Calls zu testen.Im zugehörigen Link wird das ausführlicher erklärt.
Diese Funktion ist wirklich erstaunlich, und ich wünschte, ich hätte sie schon früher gekannt.
Weil es keine Möglichkeit gab, Fehlerzweige zu testen, habe ich manchmal Teile einer Funktion vorübergehend durch Testcode ersetzt; mit diesem Trick scheint ein viel eleganterer Ansatz möglich zu sein.
Die Methode wirkt wirklich nützlich.
Ich frage mich, ob es unter Windows etwas Vergleichbares gibt.
Für Service-Health-Checks wird vorgeschlagen, sowohl eine maximale Timeout-Dauer als auch eine maximale Anzahl von Wiederholungen festzulegen.
Üblicherweise versucht man es bis zu X-mal erneut und betrachtet den Check spätestens nach Y Zeit als fehlgeschlagen.
Es wird betont, dass man möglichst schnell scheitern sollte, statt zu lange zu warten.
Bei Standard-Services starten Health Checks erst dann, wenn Container-Abhängigkeiten ausreichend abgesichert sind und der Dienst betriebsbereit ist.
Verweise auf Init Container in Kubernetes,
dependsOnin AWS ECS unddepends_onin Docker Compose.Ein POSIX-Shell-Skript als Beispiel wird gezeigt.
Es wird aber auch erwähnt, dass
curldiese Funktion bereits eingebaut hat und man daher statt eines separaten Skripts einfach Folgendes verwenden kann:Es wird berichtet, dass der Befehl
timeoutauf dem Mac standardmäßig nicht verfügbar ist und deshalb mehrfach versucht wurde, einen Timeout nur mit Bash-Builtins zu implementieren.Dabei wird erklärt, dass der
sleep-Befehl in POSIX standardisiert ist und daher verwendet werden kann.Unten wird ein Beispiel für eine Timeout-Implementierung gezeigt.
Eine Funktion namens
times_upübernimmt die Timeout-Behandlung.Außerdem wird ein Testbeispiel mit 20 Durchläufen einer
for-Schleife bei 10 Sekunden Timeout gezeigt.Vor 12 Jahren wurde nach einem Ratschlag auf Stack Overflow eine ähnliche Methode implementiert.
Im Referenzlink finden sich weitere Details.
Es wurden nur Shell-Builtins und
sleepverwendet, und der Code musste unbedingt POSIX-kompatibel sein.Es wird darauf hingewiesen, dass die Bash-Syntax
{1..20}im Beispiel nicht POSIX-konform ist.Die eigene Verbesserung bestand darin, bei ausbleibendem Timeout
trueund bei eingetretenem Timeoutfalsezurückzugeben, damit sich Fehlerbehandlung im Skript einfacher umsetzen lässt.Es wird eine sehr einfache Methode gezeigt, bei der ein Befehl und
sleepparallel laufen und der Befehl nach der vorgegebenen Zeit per Signal beendet wird.Es wird ein Skriptbeispiel von vor 13 Jahren geteilt, das
read -tzur Umsetzung eines Timeouts verwendet.Link
Es wird darauf hingewiesen, dass
curlbereits das Flag--retry-connrefusedbesitzt und sich diese Funktion daher auch ohne Shell-Schleife direkt nutzen lässt.Wenn bei
bash -cVariablen übergeben werden müssen, wird empfohlen, Argumente wie folgt hinzuzufügen:Es wird erklärt, warum
"--"verwendet wird und welche Rolleargv[0]spielt.printf %qwäre ebenfalls möglich, aber ein Bourne-kompatibler Ansatz wird bevorzugt."--"wird als sehr klar verständliches Signal für das Ende von Optionen in Bash und den meisten Unix/Linux-CLIs beschrieben.Siehe auch
BusyBox entscheidet anhand des Werts von
argv[0], welches Programm ausgeführt werden soll, daher kann man Werte wie"ls","mv"oder"cp"setzen.Wenn Wiederholungslogik benötigt wird, wird meist folgender Ansatz verwendet:
Nicht besonders elegant, aber meistens korrekt; als fortgeschrittenere Variante wird exponentielles Backoff erwähnt.
Auch hinsichtlich Erweiterbarkeit bietet das Vorteile.
shellcheckempfiehlt dafür, die Variable_zu verwenden.Referenzlink
Es wird betont, dass die Funktion
eventually_succeedsje nach Situation eventuell ein Timeout oder zusätzliche Schutzmaßnahmen braucht.Eine Erinnerung daran, in POSIX-/Prozess-/IO-Kontexten stets defensiven Code zu schreiben.
Es wird erzählt, dass früher, als die Kinder noch klein waren, der folgende Befehl als eine Art Kindersicherung genutzt wurde, damit sie nur 30 Minuten lang genau ein Programm ansehen konnten:
Die Idee wird als sehr nützlich beschrieben.
Es wird erklärt, dass man die Verwendung von Inline-Befehlen oder temporären Skriptdateien nicht besonders mag, wenn Signale an Subprozesse gesendet werden müssen.
Bevorzugt wird ein Ansatz, bei dem die gewünschte komplexe Logik in einer Funktion gekapselt, exportiert und dann mit
timeout bash -cumschlossen wird.Das hängt auch mit der von aidenn0 erwähnten sicheren Übergabe von Argumenten zusammen.
"$@"verwendet werden muss.Andernfalls werden Argumente mit Leerzeichen nicht korrekt übergeben.
Dazu wird ein Beispiel mit
long_fngeteilt, an dem sich das überprüfen lässt.Es wird an einen früheren Blogbeitrag erinnert, in dem
timeouterwähnt wurde.Der zugehörige Blog wird für alle empfohlen, die sich eher für allgemeine Programmiersprachen als für Shell oder für die internen Abläufe interessieren.
Es wird von Erfahrungen berichtet, in Kubernetes-Setups Timeouts für Befehle ergänzt zu haben.
POSIX-Shell-Skripte wie
await-cmd.sh,await-http.shundawait-tcp.shseien ausgereift und in bestimmten Situationen recht nützlich.Link zum Projekt