Ошибки прерывания в подстановке команд с использованием «-o errtrace» (т.е. set -E)

Согласно этому справочнику :

-E (также -o errtrace)

Если установлено, любая ловушка в ERR наследуется функциями оболочки, подстановками команд и командами, выполняемыми в среде подсетей. В таких случаях ловушка ERR обычно не наследуется.

Однако я должен интерпретировать это неправильно, потому что следующее не работает:

#!/usr/bin/env bash # -*- bash -*- set -e -o pipefail -o errtrace -o functrace function boom { echo "err status: $?" exit $? } trap boom ERR echo $( made up name ) echo " ! should not be reached ! " 

Я уже знаю простое назначение, my_var=$(made_up_name) , выйдет из скрипта с помощью set -e (т.е. errexit).

Предполагается ли, что -E/-o errtrace работает как приведенный выше код? Или, скорее всего, я неправильно понял?

  • Любая причина использовать или учить «...» замену новой разработки?
  • Bash: замена команд несколькими командами unset / export
  • Замена команд и пробелы
  • В чем разница между заменой и трубопроводом на bash
  • Подстановки команд vs обратные сбрасывания экранов в цитируемой строке
  • Когда нужно использовать $ () при определении переменных
  • Захват stdout и stderr как отдельные переменные в раковине рыбы
  • не может понять, что не так: $ echo '' date` '
  • 3 Solutions collect form web for “Ошибки прерывания в подстановке команд с использованием «-o errtrace» (т.е. set -E)”

    Примечание: zsh будет жаловаться на «плохие шаблоны», если вы не настроите его для принятия «встроенных комментариев» для большинства примеров здесь и не запускаете их через прокси-оболочку, как я уже делал с sh <<-\CMD ,

    Итак, как я уже говорил в комментариях выше, я не знаю конкретно о set -E bash set -E , но я знаю, что совместимые с POSIX оболочки предоставляют простой способ тестирования значения, если вы этого хотите:

      sh -evx <<-\CMD _test() { echo $( ${empty:?error string} ) &&\ echo "echo still works" } _test && echo "_test doesnt fail" # END CMD sh: line 1: empty: error string + echo + echo 'echo still works' echo still works + echo '_test doesnt fail' _test doesnt fail 

    Вы уже увидите, что, хотя я использовал parameter expansion для проверки ${empty?} _test() все еще return s pass – как это проявляется в последнем echo Это происходит потому, что неудавшееся значение убивает подглазу $( command substitution ) которая содержит это, но его родительская оболочка – _test в это время – продолжает _test на грузовиках. И echo не волнует – он очень рад служить только \newline; echo \newline; echo не является тестом.

    Но рассмотрите это:

      sh -evx <<-\CMD _test() { echo $( ${empty:?error string} ) &&\ echo "echo still works" ; } 2<<-INIT ${empty?function doesnt run} INIT _test ||\ echo "this doesnt even print" # END CMD _test+ sh: line 1: empty: function doesnt run 

    Поскольку я _test()'s с предварительно оцененным параметром в этом документе INIT here-document теперь _test() даже не пытается запустить вообще. Более того, sh shell, по-видимому, полностью отказывается от призрака, и echo "this doesnt even print" даже не печатает.

    Наверное, это не то, что вы хотите.

    Это происходит потому, что параметр-расширение ${var?} Style предназначен для выхода из shell в случае отсутствия параметра, он работает следующим образом :

    ${parameter:?[word]}

    Укажите ошибку, если Null или Unset. Если параметр не установлен или равен нулю, expansion of word (или сообщение, указывающее, что оно не установлено, если слово опущено) должно быть written to standard error и shell exits with a non-zero exit status . В противном случае значение parameter shall be substituted . Интерактивная оболочка не должна выходить.

    Я не буду копировать / вставлять весь документ, но если вы хотите сбой для set but null значение, вы используете форму:

    ${var 😕 error message }

    С помощью :colon как указано выше. Если вы хотите, чтобы null значение было успешным, просто опустите двоеточие. Вы можете также отрицать его и терпеть неудачу только для заданных значений, как я покажу в какой-то момент.

    Другой запуск _test():

      sh <<-\CMD _test() { echo $( ${empty:?error string} ) &&\ echo "echo still works" ; } 2<<-INIT ${empty?function doesnt run} INIT echo "this runs" |\ ( _test ; echo "this doesnt" ) ||\ echo "now it prints" # END CMD this runs sh: line 1: empty: function doesnt run now it prints 

    Это работает со всеми типами быстрых тестов, но выше вы увидите, что _test() запускается из середины pipeline , и на самом деле его _test() часть command list завершается неудачно, так как ни одна из команд внутри функции не работает, следующее echo запускается вообще, хотя также показано, что он может быть легко протестирован, потому что echo "now it prints" .

    Думаю, дьявол в деталях. В приведенном выше случае оболочка, которая выходит, не является _main | logic | pipeline скрипта _main | logic | pipeline _main | logic | pipeline _main | logic | pipeline но ( subshell in which we ${test?} ) || поэтому требуется небольшая песочница.

    И это может быть не очевидно, но если вы хотите пройти только для противоположного случая или только set= values, это довольно просто:

      sh <<-\CMD N= #N is NULL _test=$N #_test is also NULL and v="something you would rather do without" ( #this subshell dies echo "v is ${v+set}: and its value is ${v:+not NULL}" echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}" ${_test:+${N:?so you test for it with a little nesting}} echo "sure wish we could do some other things" ) ( #this subshell does some other things unset v #to ensure it is definitely unset echo "But here v is ${v-unset}: ${v:+you certainly wont see this}" echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}" ${_test:+${N:?is never substituted}} echo "so now we can do some other things" ) #and even though we set _test and unset v in the subshell echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}" # END CMD v is set: and its value is not NULL So this $_test:= will equal something you would rather do without sh: line 7: N: so you test for it with a little nesting But here v is unset: So this $_test:= will equal NULL so now we can do some other things _test is still NULL and v is still something you would rather do without 

    В приведенном выше примере используются все 4 формы подстановки параметров POSIX и их различные :colon null или not null тесты с :colon null . В приведенной выше ссылке есть дополнительная информация, и здесь она снова .

    И я полагаю, что мы должны показать, что наша функция работы тоже есть, верно? Мы просто объявляем empty=something как параметр нашей функции (или в любое время заранее):

      sh <<-\CMD _test() { echo $( echo ${empty:?error string} ) &&\ echo "echo still works" ; } 2<<-INIT ${empty?tested as a pass before function runs} INIT echo "this runs" >&2 |\ ( empty=not_empty _test ; echo "yay! I print now!" ) ||\ echo "suspiciously quiet" # END CMD this runs not_empty echo still works yay! I print now! 

    Следует отметить, что эта оценка стоит одна – она ​​не требует дополнительных тестов для отказа. Еще несколько примеров:

      sh <<-\CMD empty= ${empty?null, no colon, no failure} unset empty echo "${empty?this is stderr} this is not" # END CMD sh: line 3: empty: this is stderr sh <<-\CMD _input_fn() { set -- "$@" #redundant echo ${*?WHERES MY DATA?} #echo is not necessary though shift #sure hope we have more than $1 parameter : ${*?WHERES MY DATA?} #: do nothing, gracefully } _input_fn heres some stuff _input_fn one #here # shell dies - third try doesnt run _input_fn you there? # END CMD heres some stuff one sh: line :5 *: WHERES MY DATA? 

    И наконец, мы возвращаемся к исходному вопросу: как обрабатывать ошибки в подзаголовке $(command substitution) ? Правда в том, что есть два пути, но ни один из них не является прямым. Ядром проблемы является процесс оценки оболочки – расширения оболочки (в том числе $(command substitution) ) происходят раньше в процессе оценки оболочки, чем текущее выполнение команды оболочки – это когда ваши ошибки могут быть пойманы и захвачены.

    Проблема в опыте заключается в том, что к тому моменту, когда текущая оболочка оценивает ошибки, подглавная $(command substitution) уже была заменена – ошибок не осталось.

    Итак, каковы два пути? Либо вы делаете это явно в подзаголовке $(command substitution) с помощью тестов, как и без него, или вы поглощаете его результаты в текущую переменную оболочки и проверяете ее значение.

    Способ 1:

      echo "$(madeup && echo \: || echo '${fail:?die}')" |\ . /dev/stdin sh: command not found: madeup /dev/stdin:1: fail: die echo $? 126 

    Способ 2:

      var="$(madeup)" ; echo "${var:?die} still not stderr" sh: command not found: madeup sh: var: die echo $? 1 

    Это приведет к сбою независимо от количества переменных, объявленных в строке:

      v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}" sh: command not found: madeup sh: v1: parameter not set 

    И наше возвращаемое значение остается постоянным:

      echo $? 1 

    ТЕПЕРЬ ТРАП:

      trap 'printf %s\\n trap resurrects shell!' ERR v1="$(madeup)" v2="$(printf %s\\n shown after trap)" echo "${v1:?#1 - still stderr}" "${v2:?invisible}" sh: command not found: madeup sh: v1: #1 - still stderr trap resurrects shell! shown after trap echo $? 0 

    Если установлено, любая ловушка в ERR наследуется функциями оболочки, подстановками команд и командами, выполняемыми в среде подсетей

    В вашем скрипте выполняется выполнение команды ( echo $( made up name ) ). В командах bash также ограничены ; или с новой строкой . В команде

     echo $( made up name ) 

    $( made up name ) считается частью команды. Даже если эта часть выходит из строя и возвращается с ошибкой, вся команда выполняется успешно, поскольку echo не знает об этом. Когда команда возвращает с 0, не запускается ловушка.

    Вам нужно поместить его в две команды, назначение и эхо

     var=$(made_up_name) echo $var 

    Пожалуйста, рассмотрите разницу в выходе следующих наборов команд, которые используют ? параметр, расширяющийся до статуса выхода последнего выполненного переднего плана:

     # grep . $(made) $?; echo $? ${PIPESTATUS[@]} bash: made: command not found grep: 127: No such file or directory 2 2 # echo $(made) $?; echo $? ${PIPESTATUS[@]} bash: made: command not found 127 0 0 

    echo bash всегда возвращает 0:

    echo [-neE] [arg …]

    Вывод аргументов, разделенных пробелами, заканчивается новой строкой. Статус возврата всегда равен 0. (…)

    Замена команд происходит в среде подсетей и, кроме того , если команда не найдена, дочерний процесс, созданный для ее выполнения, возвращает статус 127 . И так было в наших примерах. Команда grep выходит со статусом 2 (произошла ошибка, не связанная с поиском строк). Поведение команды echo предсказуемо, поскольку оно описано в руководстве bash: оно возвращает 0, что происходит с расширением его аргументов. Таким образом, в вашем примере нет ошибки для ловушки.

    Простая команда: напоминание

    Вы упомянули, что присваивание переменной, содержащее подстановку команд, для команды, которая не существует, вызовет выход из оболочки с помощью set -e . Это не относится к необязательному присваиванию переменной, предшествующему имени команды в простой команде:

     # var=$(made) echo $?; echo $? ${PIPESTATUS[@]} bash: made: command not found 0 0 0 

    Но действительно имеет место простая команда, которая не содержит имени команды, т.е. только присваивание переменных:

     var=$(made); echo $? ${PIPESTATUS[@]} bash: made: command not found 127 127 

    но не в том случае, если в этих назначениях есть еще одна подстановка команд, а последняя успешно выполнена:

     var=$(made) var2=$(ls); echo $? ${PIPESTATUS[@]} bash: made: command not found 0 0 

    Опять же, это поведение полностью задокументировано :

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

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

    Вывод

    Сохранение одного присваивания переменной в каждой строке должно допускать, чтобы любая подзапроса команды содержалась в ловушке в случае ошибки. Встроенный echo возвращает 0 в качестве статуса выхода, а статус выхода его аргументов никогда не может испортить это. Оболочка может выйти из строя по другим причинам, например, в случае конкретного метода, предназначенного для проверки того, установлены ли переменные (как показывает другой ответ на этот вопрос), который будет оцениваться ранее в порядке выполнения. Но факт остается фактом: поведение оболочки, когда команда не найдена, независимо от того, есть или нет внутри подстановки команды, предсказуема и документирована – дочерний процесс возвращает 127, но продолжается эхо. Вот почему замена должна быть проверена либо в одном задании, либо с помощью некоторых других средств. Это понимание является предпосылкой для изучения того, какая разница в set параметров в отношении ловушки в вашем скрипте.

    Linux и Unix - лучшая ОС в мире.