Am besten verwenden Sie den timeoutBefehl, 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 timeoutVerlassen ausgelöst wird ( wodurch dieser Alarm effektiv gelöscht wird ). Während dieses kleinen Fensters timeoutkö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). timeoutAuß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 timelimitBefehl unter http://devel.ringlet.net/sysutils/timelimit/ ( timeouteinige 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, SIGALRMwenn 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 timelimitBefehl. Diese Methode ist um Jahrzehnte älter als alle anderen, hat jedoch einen anderen Ansatz, funktioniert jedoch bei gestoppten Befehlen nicht ordnungsgemäß und gibt 1bei 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 psAbrufen des Status dieses Prozesses und dem killBeenden), 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 killwürde einen Fehler melden). bash‚s killzumindest 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 sleephängenden Prozess auch nach dem cmdgestorben ist , mit bashoder ksh93ist ein Rohr zu verwenden , mit read -tstatt 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 cmdausgegangen, dass fd 4 nicht geschlossen wird.
Sie können versuchen, eine rennfreie Lösung perlwie 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.