Najlepiej byłoby użyć timeoutpolecenia, jeśli je masz:
timeout 86400 cmd
Obecna implementacja GNU (8.23) działa przynajmniej przy użyciu alarm()lub równoważnej podczas oczekiwania na proces potomny. Wydaje się, że nie chroni przed SIGALRMdostarczeniem pomiędzy waitpid()powrotem a timeoutwyjściem (skuteczne anulowanie tego alarmu ). Podczas tego małego okna timeoutmoże nawet pisać wiadomości na stderr (na przykład, jeśli dziecko zrzuci rdzeń), co jeszcze bardziej powiększy to okno wyścigu (na czas nieokreślony, jeśli stderr jest na przykład pełną potokiem).
Osobiście mogę żyć z tym ograniczeniem (które prawdopodobnie zostanie naprawione w przyszłej wersji). timeoutdołoży także starań, aby zgłosić poprawny status wyjścia, obsługiwać inne przypadki narożne (takie jak SIGALRM zablokowane / ignorowane przy uruchamianiu, obsługiwać inne sygnały ...) lepiej niż prawdopodobnie robiłbyś to ręcznie.
Dla przybliżenia możesz napisać w następujący sposób 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
Na stronie http://devel.ringlet.net/sysutils/timelimit/ znajduje się timelimitpolecenie (poprzedza GNU o kilka miesięcy).timeout
timelimit -t 86400 cmd
Ten używa alarm()podobnego mechanizmu, ale instaluje moduł obsługi SIGCHLD(ignorując zatrzymane dzieci) w celu wykrycia śmierci dziecka. Anuluje również alarm przed uruchomieniem waitpid()(nie anuluje dostarczenia, SIGALRMjeśli był w toku, ale sposób, w jaki jest napisany, nie widzę problemu) i zabija przed wywołaniem waitpid()(więc nie mogę zabić ponownie wykorzystanego pid ).
netpipes ma również timelimitpolecenie. To, że wyprzedza wszystkie pozostałe o dziesięciolecia, przyjmuje jeszcze inne podejście, ale nie działa poprawnie dla zatrzymanych poleceń i zwraca 1status wyjścia po upływie limitu czasu.
Jako bardziej bezpośrednią odpowiedź na twoje pytanie możesz zrobić coś takiego:
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
To znaczy, sprawdź, czy proces jest nadal naszym dzieckiem. Znów jest małe okno wyścigu (pomiędzy psodzyskaniem statusu tego procesu a killzabiciem go), podczas którego proces może umrzeć, a jego pid może zostać ponownie wykorzystany przez inny proces.
Z niektórych muszli ( zsh, bash, mksh), można przekazać widowisko pracy zamiast PID.
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
Działa to tylko wtedy, gdy spawnujesz tylko jedno zadanie w tle (w przeciwnym razie uzyskanie właściwego rodzaju zadania nie zawsze będzie możliwe w sposób niezawodny).
Jeśli to jest problem, po prostu uruchom nową instancję powłoki:
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
Działa to, ponieważ powłoka usuwa zadanie ze stołu zadań po śmierci dziecka. Tutaj nie powinno być żadnego okna wyścigu, ponieważ do czasu wywołania powłoki kill()albo sygnał SIGCHLD nie został obsłużony, a pid nie mógł zostać ponownie użyty (ponieważ nie był oczekiwany), lub został obsłużony, a zadanie zostało usunięte z tabeli procesów (i killzgłosiłoby błąd). bash„s killco najmniej bloków SIGCHLD zanim dostęp swoją tabelę pracy, aby rozwinąć %i odblokowuje to po kill().
Innym rozwiązaniem, aby uniknąć tego sleepprocesu wiszące wokół nawet po cmdumarł, z bashlub ksh93jest użycie rurę read -tzamiast sleep:
{
{
cmd 4>&1 >&3 3>&- &
printf '%d\n.' "$!"
} | {
read p
read -t 86400 || kill "$p"
}
} 3>&1
Ten nadal ma warunki wyścigu i tracisz status wyjścia z polecenia. Zakłada również, cmdże nie zamyka swojego fd 4.
Możesz spróbować wdrożyć rozwiązanie bez wyścigu w perl:
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...
(choć należałoby go ulepszyć, aby obsługiwał inne typy skrzynek narożnych).
Inną bez rasową metodą może być użycie grup procesów:
set -m
((sleep 86400; kill 0) & exec cmd)
Należy jednak pamiętać, że korzystanie z grup procesów może mieć skutki uboczne, jeśli zaangażowane jest we / wy do urządzenia końcowego. Ma jednak tę dodatkową zaletę, że zabija wszystkie inne dodatkowe procesy odradzane przez cmd.