Почему ловушка EXIT в bash-подоболочках не всегда вызывается? - PullRequest
0 голосов
/ 06 июня 2018

Я вижу странное поведение с bash и перехватом EXIT внутри подоболочек.Я ожидаю, что четыре следующие строки будут выводить одно и то же («hi trapped»):

a=$(trap 'echo trapped' EXIT ; echo hi); echo $a
a=$(trap 'echo trapped' EXIT && echo hi); echo $a
a=$(trap 'echo trapped' EXIT ; /bin/echo hi); echo $a
a=$(trap 'echo trapped' EXIT && /bin/echo hi); echo $a

Первые три выдают «hi trapped», но не последнюю.Он просто выводит «привет».Ловушка не вызывается.Вы можете проверить это с помощью set -x:

set -x; a=$(trap 'echo trapped' EXIT ; echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT ; /bin/echo hi); set +x; echo $a
set -x; a=$(trap 'echo trapped' EXIT && /bin/echo hi); set +x; echo $a

Путем проб и ошибок я обнаружил, что ловушка EXIT не вызывается при следующих условиях:

  1. Вся программа subshell представляет собой список команд, связанных вместе с &&.
    • Если вы в любой момент используете ; или даже ||, ловушка будет выполнена.
  2. Все команды в цепочке должны выполняться.
    • Если какая-либо из команд (кроме последней) завершается с ненулевым статусом выхода, так что последняя команда никогда не выполняется, прерывание будет выполнено.
  3. Последняя команда должна быть программой в системе, а не встроенной оболочкой и не функцией.
    • Не финальные команды могут быть встроенными или функциональными, и ловушка не будет работать, пока последняя команда является программой

Это преднамеренно?Это задокументировано?

Для справки, я сталкивался с этим, потому что rvm перезаписывает cd своей собственной функцией, которая заканчивает тем, что добавляет ловушку на EXIT, которая (среди прочего)echo -n 'Saving session...'.Я запускал сценарий оболочки, который использует эту идиому bash :

some_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )

Так что some_dir получал 'Saving session ...' добавленный к нему.Его было трудно отладить, потому что не всегда запускались подоболочки, которые добавляла EXIT trap rvm.

1 Ответ

0 голосов
/ 16 августа 2018

Я использовал strace -e clone,execve -f -p $$&, чтобы увидеть, что делает текущая оболочка при запуске echo-версии и / bin / echo-версии.Я поставил &, чтобы он продолжал читать команды.

В версии / bin / echo я считаю, что bash сделал ярлык и выполнил подпрограмму () для / bin / echo, поэтомуловушка больше не существует (ловушки не выживают execve, я полагаю).

В голой версии echo это встроенная оболочка, поэтому выполнять ее не нужно, поэтому подоболочка current () завершается какshell, и ловушка называется.

Теперь, еще одна странная вещь, если я сделаю это: bash -c 'a=$(trap "echo trapped" EXIT && /bin/echo hi); echo $a', вы увидите, что она захвачена!

Я думаю, это потому, что bash делаетярлык только в интерактивном режиме.Другое примерное отличие между пакетным режимом и интерактивным режимом - for x in $(seq 1 30); sleep 1; done.Если вы введете его в терминал и сразу нажмете Cz и нажмете fg, чтобы вернуть его, вы увидите, что он немедленно выйдет - остальные спящие пропускаются.Если вы поместите его в скрипт, а Cz, fg, он будет продолжать спать для оставшихся циклов.

...