Jak zabić proces potomny po określonym czasie w Bash?
Mam skrypt bash, który uruchamia proces potomny, który zawiesza się (właściwie zawiesza) od czasu do czasu i bez wyraźnego powodu(zamknięte źródło, więc nie ma wiele mogę z tym zrobić). W rezultacie chciałbym być w stanie uruchomić ten proces na określoną ilość czasu, oraz uśmiercić go jeśli nie powróci on pomyślnie po danej ilości czasu.
Czy istnieje Prosty i solidny sposób na osiągnięcie tego za pomocą bash?
P. S.: powiedz mi czy to pytanie jest lepiej nadaje się do serverfault lub superuser.
8 answers
(Jak widać w: Bash FAQ wpis # 68: "jak uruchomić polecenie i mieć je przerwane (timeout) po N sekundach?")
Jeśli nie masz nic przeciwko pobraniu czegoś, użyj timeout
(sudo apt-get install timeout
) i używać go jak: (większość systemów ma już zainstalowany inaczej używać sudo apt-get install coreutils
)
timeout 10 ping www.goooooogle.com
Jeśli nie chcesz czegoś pobierać, zrób to, co timeout robi wewnętrznie:
( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec ping www.goooooogle.com )
W przypadku, gdy chcesz wykonać limit czasu dla dłuższego kodu bash, użyj drugiej opcji jako takiej:
( cmdpid=$BASHPID;
(sleep 10; kill $cmdpid) \
& while ! ping -w 1 www.goooooogle.com
do
echo crap;
done )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-30 12:09:26
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) &
Lub aby uzyskać również kody wyjścia:
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) & waiter=$!
# wait on our worker process and return the exitcode
exitcode=$(wait $pid && echo $?)
# kill the waiter subshell, if it still runs
kill -9 $waiter 2>/dev/null
# 0 if we killed the waiter, cause that means the process finished before the waiter
finished_gracefully=$?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 22:46:20
sleep 999&
t=$!
sleep 10
kill $t
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 22:35:53
Też miałem takie pytanie i znalazłem jeszcze dwie rzeczy bardzo przydatne:
- zmienna SECONDS w bash.
- polecenie "pgrep".
Więc używam czegoś takiego w wierszu poleceń (OSX 10.9):
ping www.goooooogle.com & PING_PID=$(pgrep 'ping'); SECONDS=0; while pgrep -q 'ping'; do sleep 0.2; if [ $SECONDS = 10 ]; then kill $PING_PID; fi; done
Ponieważ jest to pętla, dodałem "sleep 0.2", aby utrzymać chłodzenie procesora. ;-)
(BTW: ping i tak jest złym przykładem, wystarczy użyć wbudowanej opcji" - t " (timeout).)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-08 00:37:17
Zakładając, że masz (lub możesz łatwo utworzyć) plik pid do śledzenia PID dziecka, możesz następnie utworzyć skrypt, który sprawdza czas modyfikacji pliku pid i zabija / odradza proces w razie potrzeby. Następnie po prostu umieść skrypt w crontabie, aby działał w przybliżeniu w wymaganym okresie.
Daj znać, jeśli potrzebujesz więcej szczegółów. Jeśli to nie brzmi, jakby pasowało do Twoich potrzeb, co z upstartem?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 22:33:27
Jednym ze sposobów jest uruchomienie programu w subshell i komunikowanie się z subshellem za pomocą nazwanego potoku za pomocą polecenia read
. W ten sposób możesz sprawdzić status zakończenia uruchamianego procesu i przekazać go z powrotem przez przewód.
Oto przykład wyłączenia komendy yes
po 3 sekundach. Pobiera PID procesu za pomocą pgrep
(prawdopodobnie działa tylko na Linuksie). Istnieje również pewien problem z użyciem rury, ponieważ proces otwierania rury do odczytu powiesi się, aż do jej jest również otwarty do zapisu i vice versa. Aby zapobiec zawieszeniu polecenia read
, "zaklinowałem" rurę do odczytu z podkładką w tle. (Inny sposób, aby zapobiec zamrożeniu, aby otworzyć rurę do odczytu i zapisu, tj. read -t 5 <>finished.pipe
- jednak to również może nie działać z wyjątkiem Linuksa.)
rm -f finished.pipe
mkfifo finished.pipe
{ yes >/dev/null; echo finished >finished.pipe ; } &
SUBSHELL=$!
# Get command PID
while : ; do
PID=$( pgrep -P $SUBSHELL yes )
test "$PID" = "" || break
sleep 1
done
# Open pipe for writing
{ exec 4>finished.pipe ; while : ; do sleep 1000; done } &
read -t 3 FINISHED <finished.pipe
if [ "$FINISHED" = finished ] ; then
echo 'Subprocess finished'
else
echo 'Subprocess timed out'
kill $PID
fi
rm finished.pipe
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-10 14:45:52
Oto próba, która próbuje uniknąć zabicia procesu po jego zakończeniu, co zmniejsza szansę zabicia innego procesu o tym samym identyfikatorze procesu(chociaż prawdopodobnie niemożliwe jest całkowite uniknięcie tego rodzaju błędu).
run_with_timeout ()
{
t=$1
shift
echo "running \"$*\" with timeout $t"
(
# first, run process in background
(exec sh -c "$*") &
pid=$!
echo $pid
# the timeout shell
(sleep $t ; echo timeout) &
waiter=$!
echo $waiter
# finally, allow process to end naturally
wait $pid
echo $?
) \
| (read pid
read waiter
if test $waiter != timeout ; then
read status
else
status=timeout
fi
# if we timed out, kill the process
if test $status = timeout ; then
kill $pid
exit 99
else
# if the program exited normally, kill the waiting shell
kill $waiter
exit $status
fi
)
}
Użyj jak run_with_timeout 3 sleep 10000
, który działa sleep 10000
, ale kończy się po 3 sekundach.
Jest to podobnie jak inne odpowiedzi, które używają czasu w tle, aby zabić proces potomny po opóźnieniu. Myślę, że to prawie to samo, co Dan ' s extended odpowiedź ( https://stackoverflow.com/a/5161274/1351983 ), z tym, że powłoka timeout nie zostanie zabita, jeśli już się skończyła.
Po zakończeniu tego programu, nadal będzie działać kilka trwających procesów "uśpienia", ale powinny one być nieszkodliwe.
Może to być lepsze rozwiązanie niż moja inna odpowiedź, ponieważ nie używa on nie przenośnej funkcji powłoki read -t
i nie używa pgrep
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-29 19:08:53
Oto trzecia odpowiedź, którą tu nadesłałem. Ten obsługuje przerwania sygnału i czyści procesy w tle po odebraniu SIGINT
. Używa triku $BASHPID
i exec
użytego w odpowiedzi top, aby uzyskać PID procesu (w tym przypadku $$
w wywołaniu sh
). Używa FIFO do komunikacji z subshellem, który jest odpowiedzialny za zabijanie i sprzątanie. (To jest jak rura w mojej drugiej odpowiedzi, ale posiadanie nazwanej rury oznacza, że obsługa sygnału może napisz też do niego.)
run_with_timeout ()
{
t=$1 ; shift
trap cleanup 2
F=$$.fifo ; rm -f $F ; mkfifo $F
# first, run main process in background
"$@" & pid=$!
# sleeper process to time out
( sh -c "echo \$\$ >$F ; exec sleep $t" ; echo timeout >$F ) &
read sleeper <$F
# control shell. read from fifo.
# final input is "finished". after that
# we clean up. we can get a timeout or a
# signal first.
( exec 0<$F
while : ; do
read input
case $input in
finished)
test $sleeper != 0 && kill $sleeper
rm -f $F
exit 0
;;
timeout)
test $pid != 0 && kill $pid
sleeper=0
;;
signal)
test $pid != 0 && kill $pid
;;
esac
done
) &
# wait for process to end
wait $pid
status=$?
echo finished >$F
return $status
}
cleanup ()
{
echo signal >$$.fifo
}
Starałem się unikać warunków wyścigowych. Jednak jednym ze źródeł błędu, którego nie mogłem usunąć, jest to, że proces kończy się w tym samym czasie, co timeout. Na przykład, run_with_timeout 2 sleep 2
lub run_with_timeout 0 sleep 0
. Dla mnie ten drugi daje błąd:
timeout.sh: line 250: kill: (23248) - No such process
Ponieważ próbuje zabić proces, który już się zakończył.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-04 12:07:38