Работает ли ловушка должным образом во время прокачки?
Вот минимальный код для демонстрации проблемы: http://pastebin.com/5TXDpSh5
#!/bin/bash
set -e
set -o pipefail
function echoTraps() {
echo "= on start:"
trap -p
trap -- 'echo func-EXIT' EXIT
echo "= after set new:"
trap -p
# we can ensure after script done - file '/tmp/tmp.txt' was not created
trap -- 'echo SIG 1>/tmp/tmp.txt' SIGPIPE SIGHUP SIGINT SIGQUIT SIGTERM
}
trap -- 'echo main-EXIT1' EXIT
echo "===== subshell trap"
( echoTraps; )
echo "===== pipe trap"
echoTraps | cat
echo "===== done everything"
выход
===== subshell trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT
===== pipe trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
===== done everything
main-EXIT1
ожидаемый результат
===== subshell trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT
===== pipe trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT <---- here is the expected difference
===== done everything
main-EXIT1
Примечание: я тестировал OSX 10.9.2 bash (3.2.51) - другие версии bash имеют одинаковую разницу между фактическим ожидаемым выходом и описанным ниже.
2 ответа
Единственный способ выяснить, ожидается ли такое поведение или нет, - спросить у Чета Рами (GNU bash сопровождающего). Пожалуйста, отправьте электронное письмо со своим отчетом на bug-bash@gnu.org
Вы можете видеть, что текущее поведение кажется правильным, учитывая, что оно обрабатывает случай подоболочки явно в: http://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c#n621
/* We want to run the exit trap for forced {} subshells, and we
want to note this before execute_in_subshell modifies the
COMMAND struct. Need to keep in mind that execute_in_subshell
runs the exit trap for () subshells itself. */
/* This handles { command; } & */
s = user_subshell == 0 && command->type == cm_group && pipe_in == NO_PIPE && pipe_out == NO_PIPE && asynchronous;
/* run exit trap for : | { ...; } and { ...; } | : */
/* run exit trap for : | ( ...; ) and ( ...; ) | : */
s += user_subshell == 0 && command->type == cm_group && (pipe_in != NO_PIPE || pipe_out != NO_PIPE) && asynchronous == 0;
last_command_exit_value = execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close);
if (s)
subshell_exit (last_command_exit_value);
else
sh_exit (last_command_exit_value);
Как видите, явный случай подоболочки обрабатывается как особый случай (как и в случае с группировкой команд). Как выяснил Эдриан, это поведение исторически развивалось из-за многочисленных сообщений об ошибках.
Вот список изменений для этой конкретной функции (запускающая ловушку EXIT на подоболочках):
Фиксация: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=a37d979e7b706ce9babf1306c6b370c327038eb9
+execute_cmd.c
+ - execute_command_internal: make sure to run the EXIT trap for group
+ commands anywhere in pipelines, not just at the end. From a point
+ raised by Andreas Schwab <schwab@linux-m68k.org>
Отчет: https://lists.gnu.org/archive/html/bug-bash/2013-04/msg00126.html (Re: перехватывать EXIT в трубчатой подоболочке, не запущенной во время ожидания)
Фиксация: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=1a81420a36fafc5217e770e042fd39a1353a41f9
+execute_cmd.c
+ - execute_command_internal: make sure any subshell forked to run a
+ group command or user subshell at the end of a pipeline runs any
+ EXIT trap it sets. Fixes debian bash bug 698411
+ http://bugs.debian.org/cgi-big/bugreport.cgi?bug=698411
Отчет: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=698411 (ловушка EXIT, конвейер и подоболочка)
Фиксация: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=fd58d46e0d058aa983eea532bfd7d4c597adef54
+execute_cmd.c
+ - execute_command_internal: make sure to call subshell_exit for
+ {} group commands executed asynchronously (&). Part of fix for
+ EXIT trap bug reported by Maarten Billemont <lhunath@lyndir.com>
Отчет: http://lists.gnu.org/archive/html/bug-bash/2012-07/msg00084.html (ловушки EXIT в интерактивных оболочках)
Также имеется недавний отчет об ошибке в связи с тем, что ловушка EXIT не выполняется в некоторых ожидаемых контекстах: http://lists.gnu.org/archive/html/bug-bash/2016-11/msg00054.html
Вот еще несколько тестов для вашего развлечения:
$ cat traps.sh
#!/bin/bash
echoTraps() {
echo "entering echoTraps()"
printf " traps: %s\n" "$(trap -p)"
echo " setting trap"
trap -- 'echo "func-exit()"' EXIT
printf " traps: %s\n" "$(trap -p)"
echo "exiting echoTraps()"
}
trap -- 'echo "main-exit()"' EXIT
echo "===== calling '( echoTraps; )'"
( echoTraps; )
echo
echo "===== calling 'echoTraps | cat'"
echoTraps | cat
echo
echo "===== calling '( echoTraps; ) | cat'"
( echoTraps; ) | cat
echo
echo "===== calling '{ echoTraps; } | cat'"
{ echoTraps; } | cat
echo
Баш-4.2.25(1)
$ ./traps.sh
===== calling '( echoTraps; )'
entering echoTraps()
traps:
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
func-exit()
===== calling 'echoTraps | cat'
entering echoTraps()
traps: trap -- 'echo "main-exit()"' EXIT
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
===== calling '( echoTraps; ) | cat'
entering echoTraps()
traps:
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
func-exit()
===== calling '{ echoTraps; } | cat'
entering echoTraps()
traps: trap -- 'echo "main-exit()"' EXIT
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
main-exit()
Баш-4,3,0(1)
$ bash-static-4.3.2/bin/bash-static traps.sh
===== calling '( echoTraps; )'
entering echoTraps()
traps:
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
func-exit()
===== calling 'echoTraps | cat'
entering echoTraps()
traps: trap -- 'echo "main-exit()"' EXIT
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
===== calling '( echoTraps; ) | cat'
entering echoTraps()
traps:
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
func-exit()
===== calling '{ echoTraps; } | cat'
entering echoTraps()
traps: trap -- 'echo "main-exit()"' EXIT
setting trap
traps: trap -- 'echo "func-exit()"' EXIT
exiting echoTraps()
func-exit()
main-exit()
Итог: не полагайтесь на подобные случаи. Я помню, как исследовал другие несоответствия (не касательно ловушек) в отношении подоболочек и труб и пытался обернуть голову вокруг bash
исходный код, и вы не хотите идти по этому пути и пытаться понять, почему он ведет себя так, как в определенных ситуациях (кстати, код действительно ужасен). Как видите, некоторые вещи, похоже, были "исправлены" и / но оба моих примера уже ведут себя не так, как ваш.