POSIX-Äquivalent für GNU-Timeout?


14

Der GNU- timeoutBefehl coreutils ist für bestimmte Skriptsituationen äußerst praktisch. Er ermöglicht es, die Ausgabe eines Befehls zu verwenden, wenn er schnell ausgeführt werden kann, und zu überspringen, wenn er zu lange dauern würde.

Wie kann ich das grundlegende Verhalten der timeoutVerwendung nur von POSIX angegebenen Dienstprogrammen approximieren ?


(Ich denke , es eine Kombination von umfassen kann wait, sleep, killund wer weiß , was sonst noch, aber vielleicht einen einfacheren Ansatz fehlt mir.)


3
Siehe Zeitüberschreitung in einem Shell-Skript , aber ich halte dies nicht für ein Duplikat, da ich die Portierbarkeit auf Vor-POSIX-Systeme angefordert habe und die Anforderung hatte, stdin und stdout beizubehalten, was einige Lösungen mit Hintergrundprozessen ausschließen.
Gilles 'SO- hör auf böse zu sein'

command & pid=$! ; sleep 5 && kill $pid
Pandya

1
@Pandya, führt das nicht zu einer leichten Race-Bedingung, bei der bei commandschnellem Ende die Wahrscheinlichkeit gering ist, dass das System vor der Ausführung piddes killBefehls von einem anderen Prozess erneut verwendet wird . Ich würde nicht wollen, dass in Produktionscode ....
Wildcard

Können Sie mehr über das zugrunde liegende Problem erklären, das Sie lösen möchten? Warum nicht einfach das timeoutProgramm kompilieren und verwenden?
James Youngman

2
@ JamesYoungman, ich vermute, Sie sind nicht sehr vertraut mit dem Schreiben von Skripten für die Portabilität . Wenn Sie nach einer POSIX-kompatiblen (oder POSIX-spezifizierten) Methode gefragt werden, bedeutet dies, dass sie portabel sein soll. Kompilieren Quellcode in ein binäres ist ausdrücklich nicht tragbar, und je nach Sicherheitsrichtlinien des Unternehmens, können Sie nicht einen Compiler installiert haben überhaupt auf einem Produktionsserver.
Wildcard

Antworten:


2

Mein Ansatz wäre dieser:

  • Befehl als Hintergrundprozess ausführen 1

  • "Watchdog Timer" als Hintergrundprozess ausführen 2

  • Richten Sie einen Handler ein, um ein Beendigungssignal in der übergeordneten Shell abzufangen

  • Warten Sie, bis beide Prozesse abgeschlossen sind. Der Prozess, der zuerst beendet wird, sendet das Beendigungssignal an das übergeordnete Element.

  • Der Trap-Handler des Elternteils bricht beide Hintergrundprozesse über die Jobsteuerung ab (einer von ihnen wurde bereits per Definition beendet, aber dieser Kill ist ein harmloser No-Op, da wir keine PIDs verwenden, siehe unten).

Ich habe versucht, die in den Kommentaren angesprochene mögliche Race-Bedingung zu umgehen, indem ich die Job-Control-IDs der Shell (die in dieser Shell-Instanz eindeutig wären) verwendete, um die abzubrechenden Hintergrundprozesse anstelle der System-PIDs zu identifizieren.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Ergebnis für TIMEOUT=10(Befehl wird vor Watchdog beendet):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Ergebnis für TIMEOUT=1(Watchdog wird vor dem Befehl beendet):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Ergebnis für TIMEOUT=5(Watchdog und Kommando enden "fast" gleichzeitig):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution

Dies ist nicht POSIX-konform, da (a) die Funktionsdefinition sein sollte cleanup() { ...; }und (b) die Auftragssteuerung eine von POSIX nicht spezifizierte Bash-Funktion ist (obwohl ksh und andere dies auch haben). Nettes Drehbuch!
Wildcard

Mit timeout="$1"; shiftund kann man es auch besser machen(exec "$@"; echo "Command completed"; kill $$) die Argumentliste nicht neu aufteilen.
Wildcard
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.