Am besten verwenden Sie den timeout
Befehl, wenn Sie ihn haben, der dafür vorgesehen ist:
timeout 86400 cmd
Die aktuelle (8.23) GNU-Implementierung funktioniert zumindest mit alarm()
oder gleichwertig, während auf den untergeordneten Prozess gewartet wird. Es scheint nicht zu verhindern, dass der AlarmSIGALRM
zwischen der waitpid()
Rückkehr und dem timeout
Verlassen ausgelöst wird ( wodurch dieser Alarm effektiv gelöscht wird ). Während dieses kleinen Fensters timeout
können sogar Nachrichten auf stderr geschrieben werden (zum Beispiel, wenn das Kind einen Core gelöscht hat), wodurch das Race-Fenster weiter vergrößert wird (auf unbestimmte Zeit, wenn stderr zum Beispiel eine vollständige Pipe ist).
Ich persönlich kann mit dieser Einschränkung leben (die wahrscheinlich in einer zukünftigen Version behoben wird). timeout
Außerdem wird besonders darauf geachtet, den korrekten Ausgangsstatus zu melden, andere Eckfälle (wie SIGALRM beim Start blockiert / ignoriert, andere Signale ...) besser zu behandeln, als Sie es wahrscheinlich von Hand schaffen würden.
Als Annäherung könnte man es so schreiben perl
:
perl -MPOSIX -e '
$p = fork();
die "fork: $!\n" unless defined($p);
if ($p) {
$SIG{ALRM} = sub {
kill "TERM", $p;
exit 124;
};
alarm(86400);
wait;
exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
} else {exec @ARGV}' cmd
Es gibt einen timelimit
Befehl unter http://devel.ringlet.net/sysutils/timelimit/ ( timeout
einige Monate vor GNU ).
timelimit -t 86400 cmd
Dieser verwendet einen alarm()
ähnlichen Mechanismus, installiert jedoch einen Handler SIGCHLD
(ignoriert angehaltene Kinder), um das sterbende Kind zu erkennen. Außerdem wird der Alarm vor dem Ausführen abgebrochen waitpid()
(dh die Zustellung wird nicht abgebrochen, SIGALRM
wenn er aussteht, aber ich sehe kein Problem darin, dass er so geschrieben ist) und vor dem Aufrufen abgebrochen waitpid()
(eine wiederverwendete PID kann nicht getötet werden) ).
netpipes hat auch einen timelimit
Befehl. Diese Methode ist um Jahrzehnte älter als alle anderen, hat jedoch einen anderen Ansatz, funktioniert jedoch bei gestoppten Befehlen nicht ordnungsgemäß und gibt 1
bei Zeitüberschreitung einen Beendigungsstatus zurück.
Als direktere Antwort auf Ihre Frage können Sie Folgendes tun:
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
Überprüfen Sie also, ob der Prozess noch ein Kind von uns ist. Wieder gibt es ein kleines Wettlauffenster (zwischen dem ps
Abrufen des Status dieses Prozesses und dem kill
Beenden), in dem der Prozess sterben und seine PID von einem anderen Prozess wiederverwendet werden kann.
Mit einigen Muscheln ( zsh
, bash
, mksh
), können Sie Job - Spezifikationen statt pids passieren.
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
Das funktioniert nur, wenn Sie nur einen Hintergrundjob erzeugen (andernfalls ist es nicht immer zuverlässig möglich, die richtige Jobspezifikation zu erhalten).
Wenn das ein Problem ist, starten Sie einfach eine neue Shell-Instanz:
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
Das funktioniert, weil die Shell den Job vom Job-Tisch entfernt, wenn das Kind stirbt. Hier sollte es kein Rennfenster geben, da zum Zeitpunkt des Shell-Aufrufs kill()
entweder das SIGCHLD-Signal nicht verarbeitet wurde und die PID nicht wiederverwendet werden kann (da nicht darauf gewartet wurde) oder es verarbeitet wurde und Job wurde aus der Prozesstabelle entfernt (und kill
würde einen Fehler melden). bash
‚s kill
zumindest blockiert SIGCHLD , bevor er seine Arbeit Tabelle greift das zu erweitern %
und deblockiert es nach dem kill()
.
Eine weitere Möglichkeit zu vermeiden , dass mit sleep
hängenden Prozess auch nach dem cmd
gestorben ist , mit bash
oder ksh93
ist ein Rohr zu verwenden , mit read -t
statt sleep
:
{
{
cmd 4>&1 >&3 3>&- &
printf '%d\n.' "$!"
} | {
read p
read -t 86400 || kill "$p"
}
} 3>&1
Dieser hat immer noch Rennbedingungen und du verlierst den Exit-Status des Befehls. Es wird auch davon cmd
ausgegangen, dass fd 4 nicht geschlossen wird.
Sie können versuchen, eine rennfreie Lösung perl
wie folgt zu implementieren :
perl -MPOSIX -e '
$p = fork();
die "fork: $!\n" unless defined($p);
if ($p) {
$SIG{CHLD} = sub {
$ss = POSIX::SigSet->new(SIGALRM); $oss = POSIX::SigSet->new;
sigprocmask(SIG_BLOCK, $ss, $oss);
waitpid($p,WNOHANG);
exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
unless $? == -1;
sigprocmask(SIG_UNBLOCK, $oss);
};
$SIG{ALRM} = sub {
kill "TERM", $p;
exit 124;
};
alarm(86400);
pause while 1;
} else {exec @ARGV}' cmd args...
(obwohl es verbessert werden müsste, um andere Arten von Eckkoffern zu handhaben).
Eine andere rennfreie Methode könnte die Verwendung von Prozessgruppen sein:
set -m
((sleep 86400; kill 0) & exec cmd)
Beachten Sie jedoch, dass die Verwendung von Prozessgruppen Nebenwirkungen haben kann, wenn ein Endgerät über E / A verfügt. Es hat jedoch den zusätzlichen Vorteil, alle anderen zusätzlichen Prozesse, die durch hervorgerufen werden, zu beenden cmd
.