zachowanie bash na sigterm

3

Przygotuj skrypt jak poniżej:

#!/bin/bash
#
# run this script.  don't run it if it's already running.
#

PIDFILE=/tmp/script.pid
LOGFILE=script.log


if [[ -f $PIDFILE ]]; then
    echo "$PIDFILE exists.  Not going to run..."
    exit 0
fi

sleep 10m >> $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE

trap "echo Exiting...; rm $PIDFILE; exit $?" INT TERM EXIT KILL

wait $PID

Wzywam ten skrypt, jak poniżej:

timeout 2m ./test_script

Po przekroczeniu limitu czasu lub Ctrl + C skrypt wypisuje dwukrotnie „Wyjście”.

# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory

Poniżej znajduje się wynik śledzenia i więcej danych:

# ps -ef | grep -v grep | egrep -i "sleep|time"
root      8571  4690  0 12:17 pts/0    00:00:00 timeout 2m ./test_script
root      8572  8571  0 12:17 pts/0    00:00:00 /bin/bash ./test_script
root      8573  8572  0 12:17 pts/0    00:00:00 sleep 10m
# cat /tmp/script.pid
8573
# strace -p 8571
Process 8571 attached - interrupt to quit
wait4(-1, 0x7fffc43fad4c, 0, NULL)      = ? ERESTARTSYS (To be restarted)
--- SIGINT (Interrupt) @ 0 (0) ---
kill(0, SIGINT)                         = 0
kill(0, SIGCONT)                        = 0
--- SIGCONT (Continued) @ 0 (0) ---
rt_sigreturn(0)                         = 61
--- SIGINT (Interrupt) @ 0 (0) ---
rt_sigreturn(0x2)                       = 61
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 8572
--- SIGCHLD (Child exited) @ 0 (0) ---
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
Process 8571 detached

Czy ktoś może mi pomóc w zrozumieniu wewnętrznych przyczyn, dla których skrypt dwukrotnie wychwytuje sygnał, aby wydrukować „Wyjście ...” 2 razy?

cog_n1t1v3
źródło
rm: cannot remove /tmp/script.pid: No such file or directory<- wykonujesz echo "Exiting..."dwa razy, czy na pewno potrzebujesz wszystkiego INT TERM EXIT KILL- czy to możliwe, że dwa z nich zostaną uruchomione - w tym samym czasie - po naciśnięciu CTRL-C?
Hannu
prawdopodobnie tak się dzieje. dwa sygnały obsługiwane są prawie jednocześnie przez trap. O ile skrypt musi przechwytywać wszystkie 4 określone sygnały, tak, potrzebuję ich wszystkich. Alternatywnie po prostu używam limitu czasu z poziomu mojego skryptu. To timeout
omija
Nie możesz złapać KILL, żeby się nie liczyć. Jeśli jednak uruchomię skrypt przy użyciu wersji bash 4.1.5, widzę tylko jedną wiadomość dla TERM(kiedy wychodzi) i nie ma wyjścia dla INT. Być może wersja bash ma znaczenie.
Thomas Dickey

Odpowiedzi:

1

Jeśli zastąpisz swoje trapoświadczenie tymi trzema liniami:

trap "echo Exiting... INT;  exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT

dostaniesz wynik

Exiting... TERM
Exiting... EXIT

z którego możemy wywnioskować

  • trap … TERMZestawienie powoduje, że powłoka wyłapania sygnału SIGTERM. timeoutKomenda wysyła proces SIGTERM (domyślnie), gdy upłynie czas. Więc powłoka łapie sygnał i wykonuje określone polecenie, w tym echo, rm(w twoim skrypcie) i exit.
  • To trap … EXIToświadczenie powoduje, że powłoka pozostawia sobie karteczkę z napisem „pamiętaj, aby to zrobić, zanim pójdę do domu”. Tak więc, gdy pułapka SIGTERM wykonuje exitpolecenie, pułapka EXIT jest wykonywana.
  • Kiedy pułapka WYJŚCIA wykonuje exitpolecenie, skrypt faktycznie kończy działanie, zamiast wykonywać pułapkę WYJŚCIE i przechodzić w piekło rekurencyjne.

Jeśli wpiszesz Ctrl+ Cpodczas działania skryptu, otrzymasz pułapkę INT, a następnie pułapkę EXIT. Jeśli uruchomisz skrypt bez timeoutlimitu czasu lub z czasem oczekiwania dłuższym niż czas uśpienia, dostaniesz tylko pułapkę WYJŚCIA.

Prawdopodobnie wystarczy powiedzieć

trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT

Uważam, że pułapka EXIT nie musi zostać wykonana exit, ponieważ wchodzisz w pułapkę EXIT wykonując exitpolecenie (w tym ukrytą na końcu skryptu), więc po zakończeniu wykonywania pułapki EXIT ( echoi rm) powłoka nie ma już nic do roboty poza wyjściem. Jedyne pytanie brzmi, z jakim statusem wyjściowym skrypt wychodzi. A jeśli zapisujesz jakąś wartość statusu wyjścia i to robisz rm $PIDFILE; exit $saved_status, może to być interesujące. Ale dopóki mówisz rm $PIDFILE; exit $?, skrypt prawdopodobnie zakończy działanie ze statusem wyjścia rm; i to prawdopodobnie nastąpi domyślnie, jeśli rmjest to ostatnie polecenie, które wykonasz.

Zrobiłem kilka szybkich testów, które sugerowały, że można to pominąć

trap "exit" INT TERM

rozkaz, ale tego nie rozumiem. YMMV.


PS Jak powiedział Thomas, trap … KILLjest nieskuteczny.

G-Man
źródło