3

Есть сценарий, как показано ниже:

#!/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

Я использую этот скрипт, как показано ниже:

timeout 2m ./test_script

По таймауту или ctrl+c скрипт выводит "Выход" дважды.

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

Ниже приведен вывод strace и дополнительных данных:

# 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

Может кто-нибудь любезно помочь мне разобраться во внутренностях того, почему сценарий перехватывает сигнал дважды, чтобы напечатать «Выход ...» 2 раза?

1 ответ1

1

Если вы замените свой оператор trap следующими тремя строками:

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

вы получите выход

Exiting... TERM
Exiting... EXIT

из которого мы можем вывести

  • Оператор trap … TERM заставляет оболочку перехватывать сигнал SIGTERM.  Команда timeout отправляет процессу SIGTERM (по умолчанию) по истечении времени ожидания.  Таким образом, оболочка ловит сигнал и выполняет указанную команду, включая echo , rm (в вашем реальном скрипте) и exit .
  • Оператор trap … EXIT заставляет оболочку оставлять себе заметку со словами «не забудьте сделать это, прежде чем я уйду домой».  Таким образом, когда ловушка SIGTERM выполняет команду exit , ловушка EXIT выполняется.
  • Когда ловушка EXIT выполняет команду exit , сценарий фактически завершается, а не выполняет ловушку EXIT и отправляется в ад рекурсии.

Если вы нажмете Ctrl+C во время работы скрипта, вы получите ловушку INT, а затем ловушку EXIT.  Если вы запустите сценарий без timeout или с интервалом, превышающим время ожидания, вы получите только ловушку EXIT.

Это, наверное, достаточно хорошо, чтобы сказать

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

Я полагаю, что ловушке EXIT не нужно выполнять exit , потому что вы попадаете в ловушку EXIT, выполняя команду exit (включая неявную в конце скрипта), поэтому, когда вы заканчиваете выполнение ловушки EXIT (echo и rm) оболочке больше нечего делать, кроме выхода.  Вопрос только в том, с каким статусом выхода выходит сценарий.  И, если вы сохраняли какое-то значение состояния выхода и выполняли rm $PIDFILE; exit $saved_status , это может быть интересно.  Но пока вы говорите о rm $PIDFILE; exit $? возможно, скрипт завершит работу со статусом выхода из rm ; и это, вероятно, произойдет по умолчанию, если rm - последняя команда, которую вы выполняете.

Я сделал несколько быстрых тестов, которые показали, что можно оставить

trap "exit" INT TERM

команда, но я не понимаю этого.  YMMV.


PS  Как сказал Томас, trap … KILL неэффективна.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .