“захватите … ВЫХОД ТЕРМИНА INT”, действительно необходимый?

В crontab перед управлением добавляют . $HOME/.profile. Например:

0 5 * * * . $HOME/.profile; /path/to/command/to/run

Cron ничего не знает о Вашей оболочке; это запускается системой, таким образом, это имеет минимальную среду. Если Вы хотите что-нибудь, необходимо было ввести это самим.

67
09.12.2012, 01:28
4 ответа

Спецификация POSIX не говорит многое об условиях, приводящих к выполнению прерывания ВЫХОДА, только о том, на что должна быть похожей его среда, когда это выполняется.

В оболочке пепла Busybox Ваш тест выхода прерывания не повторяет 'ПРЕРЫВАНИЕ' прежде, чем выйти или из-за SIGINT или из-за SIGTERM. Я подозревал бы, что существуют другие оболочки в существовании, которое не может проложить себе путь также.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 
21
27.01.2020, 19:31
  • 1
    dash также не захватывает на просто EXIT когда это получает SIGINT/SIGTERM. –  maxschlepzig 29.10.2016, 19:50
  • 2
    zsh также - таким образом, возможно bash единственная оболочка где EXIT также соответствует сигналам. –  maxschlepzig 29.10.2016, 19:59

Да, есть разница.

Этот скрипт выйдет, когда вы нажмёте Enter, или отправите ему SIGINT или SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

Этот скрипт выйдет, когда вы нажмёте Enter:

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Tested in sh, Bash, и Zsh.   (больше не работает в sh, когда вы добавляете команду для запуска ловушки)


Также есть то, что сказал @Shawn: Ash и Dash не ловят сигналы с EXIT.

Так что, для надежной работы с сигналами, лучше всего избегать ловушек EXIT вообще, и использовать что-то вроде этого:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup
27
27.01.2020, 19:31

Уточнение последнего ответа, потому что у него есть проблемы:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Пункты выше:

Обработчики INT и TERM не завершают работу, когда я тестирую - они обрабатывают ошибку затем оболочка возвращается к завершению (и это неудивительно). Поэтому я гарантирую, что очистка завершится после этого, и в случае сигналов всегда используется код ошибки (а в другом случае нормального выхода код ошибки сохраняется).

С bash кажется, что выход в обработчике INT также вызывает обработчик EXIT, поэтому я разворачиваю обработчик выхода и вызываю его сам (который будет работать в любой оболочке независимо от поведения).

Я перехватываю exit, потому что сценарии оболочки могут завершиться до того, как достигнут конца - синтаксические ошибки, установка -e и ненулевой возврат, просто вызов exit. Вы не можете полагаться на то, что шелл-скрипт доберется до сути.

SIGQUIT - это Ctrl- \, если вы никогда его не пробовали. Предоставляет вам бонусную сердцевину. Так что я думаю, что это тоже стоит заманить в ловушку, даже если это немного неясно.

Прошлый опыт говорит, что если вы (как и я) всегда нажимаете Ctrl-C несколько раз, вы иногда поймаете это на полпути через часть очистки вашего сценария оболочки, так что это работает, но не всегда так идеально, как хотелось бы .

12
27.01.2020, 19:31

Это зависит от того, чего вы пытаетесь достичь и какие снаряды вы используете. Для bashможно просто использовать EXIT. Но не все оболочки вызывают обработчик EXITдля SIGINT/SIGTERM.

Для них можно попробовать задать один обработчик на несколько сигналов (trap '...' INT EXIT), но тогда он может вызываться несколько раз:

$ bash -c 'trap "echo trap" INT EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 276923
trap
trap
[1]+  Done                    bash -c 'trap "echo trap" INT EXIT; sleep 3'

Так что либо вы пишете это с учетом этого, либо вы можете попытаться перенаправить все EXITобработчику:

$ bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 286229
EXIT 123
[1]+  Exit 123                bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3'

Но если вы настроили обработчик для SIGINT, обычно вы хотите, чтобы он убивал скрипт с помощьюSIGINT:

a.sh:

trap 'exit 123' INT
trap 'echo EXIT $?; trap - INT; kill -INT $$' EXIT
sleep 3
$ bash h.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 236263
EXIT 123
[1]+  Interrupt               bash h.sh

А в Debian < 10(dash < 0.5.10)сигнал , убивающий скрипт (, если какой-либо)не передан .

Решение, которое я придумал:

set -eu
cleanup() {
    echo "cleanup ($1)"
    trap - INT TERM EXIT  # avoid reexecuting handlers
    if [ "$1" = 130 ]; then
        kill -INT $$
    elif [ "$1" = 143 ]; then
        kill -TERM $$
    else
        exit "$1"
    fi
}
trap 'cleanup 130' INT
trap 'cleanup 143' TERM
trap 'cleanup $?' EXIT

if [ "${1-}" = fail ]; then
    no-such-command
fi
sleep 3
$ bash f.sh; echo $?
cleanup (0)
0

$ bash f.sh fail; echo $?
f.sh: line 20: no-such-command: command not found
cleanup (127)
127

$ bash f.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 282422
cleanup (130)
[1]+  Interrupt               bash f.sh

$ bash f.sh & pid=$!; sleep 1; kill -TERM $pid; wait $pid
[1] 282458
cleanup (143)
[1]+  Terminated              bash f.sh

Испытано в:

  • bash:5.1.8
  • тире :0.5.10, 0.5.8,0.5.7
  • Alpine Linux 3.14(busybox)
0
10.11.2021, 02:09

Теги

Похожие вопросы