Ловушка ERR не выполняется при синтаксической ошибке - PullRequest
2 голосов
/ 10 октября 2019

Согласно man bash,

set -e

Выйти немедленно, если (отрывок). Ловушка на ERR, если установлена, выполняется до выхода из оболочки.

Однако приведенный ниже скрипт не вызывает ловушку ERR.

trap 'echo ERR; sleep 1' ERR
trap 'echo EXIT; sleep 1' EXIT

set -e

array=(a b c)
echo $(( ${#array[@] - 1)) #note the closing bracket } is forgotten

echo "after"

Ожидаемый результат:

$ bash script.sh 
4.sh: line 7:  ${#array[@] - 1: bad substitution
ERR
EXIT
# and then shell exits

Фактический результат:

$ bash script.sh 
4.sh: line 7:  ${#array[@] - 1: bad substitution
EXIT
# and then shell exits

Если я удалю строку set -e, то

$ bash script2.sh
4.sh: line 7:  ${#array[@] - 1: bad substitution
after #the last echo command is executed
EXIT

Это означает set -eловит синтаксическую ошибку. Почему не вызывается ловушка ERR, хотя оболочка не завершает работу?


Среда:

Я тестировал скрипт на двух машинах.

$ bash --version
GNU bash, version 4.4.12(1)-release (arm-unknown-linux-gnueabihf)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ bash --version
GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Дополнение:

Согласно опубликованным ответам, вполне естественно, что ловушка не выполняется, и это потому, что echo $(( ${#array[@] - 1)) не завершает свое выполнение, чтобы вернутьсястатус выхода. Правильно ли мое понимание?

Однако man bash объясняет set -e как

Немедленно завершить работу, если конвейер (который может состоять из одной простой команды), список илисоставная команда (см. ОБРАЗЕЦ ОБОЛОЧКИ выше), выход с ненулевым статусом.

Я думаю, что для этого также требуется выполнение команды. Если echo $(( ${#array[@] - 1)) не завершит свое выполнение, я считаю, что set -e здесь не должно работать, а echo "after" должно быть выполнено.


Дополнение 2:

Согласно Комментарий oguz ismail , это проблема с документацией. POSIX говорит, что set -e должен выйти из оболочки, когда

  • ненулевой статус выхода (как вы можете видеть в man bash)

  • или ошибка оболочки возникает

и ошибка оболочки включает синтаксическую ошибку . Но в man bash отсутствует объяснение второго случая, верно? Если это так, все согласуется, за исключением того факта, что man bash не объясняет реализацию точно и утверждения «Это те же условия, которым подчиняется опция errexit (-e)». найдено в объяснении trap в man bash.

Ответы [ 2 ]

2 голосов
/ 10 октября 2019

Это из-за странного способа реализации set -e в bash за эти годы. Он поставляется с множеством конкретных случаев, в которых он должен работать.

С man bash:

  • trap ... ERR
    • Если sigspec равен ERR , команда arg выполняется всякий раз, когда конвейер (который может состоять из одной простой команды) , список или составная команда возвращает ненулевой выходстатус, при соблюдении следующих условий:
      • Ловушка ERR не выполняется, если сбойная команда является частью списка команд, следующих сразу за ключевым словом while или until ...
      • ... часть теста в операторе if ...
      • ... часть команды, выполненной в списке && или ||, за исключением следующей командыконечная && или || ...
      • ... любая команда в конвейере, кроме последней ...
      • ... или если обратное значение команды инвертируетсяиспользование !.
    • Это те же условия, которым подчиняется опция errexit -e.

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

Но когда строка echo $(( ${#array[@] - 1)) имеет видВскоре после сбоя арифметического расширения не существует команды, которая фактически запускала бы ловушку ERR, потому что для запуска ловушки команда должна быть завершена. Со страницы man bash

  • Если Bash ожидает завершения команды и получает сигнал, для которого установлена ​​ловушка, ловушка не будет выполнена, пока команда не завершится.
1 голос
/ 10 октября 2019

Ловушка ERR никогда не перехватывает синтаксические ошибки и не предназначена для этого. Начиная с man bash:

  If  a  sigspec  is  ERR,  the command arg is executed whenever a
  pipeline (which may consist of a single simple command), a list,
  or a compound command returns a non-zero exit status ...

, в этом случае команда никогда не выполняется, поэтому не возвращает ненулевое состояние, сценарий до этого завершается ошибкой.

...