Примечание: zshбудет жаловаться на «плохие шаблоны», если вы не настроите его для принятия «встроенных комментариев» для большинства примеров здесь и не будете запускать их через прокси-оболочку, как я это сделал sh <<-\CMD.
Итак, как я уже говорил в комментариях выше, я не знаю конкретно о bashset -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()еще returnS проход - как в проявили последнее echoЭто происходит потому , что не удалось значение убивает $( command substitution )подоболочку , который содержит его, но его родительские оболочки - _testв это время - продолжает грузоперевозку. И 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оболочка, по-видимому, полностью отдает призрак и echo "this doesnt even print" даже не печатает.
Вероятно, это не то, что вы хотите.
Это происходит потому, что ${var?}расширение параметра style предназначено для выходаshell в случае отсутствия параметра, оно работает следующим образом :
${parameter:?[word]}
Указать ошибку, если Nullили Unset.если параметр не установлен или имеет значение NULL, то 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сбоев, и фактически его содержащая command listподоболочка сбоит полностью, так как ни одна из команд в функции не выполняется, ни следующий echoзапуск вообще, хотя также показано, что это может быть легко проверено, потому что echo "now it prints" теперь печатает.
Полагаю, дьявол кроется в деталях. В приведенном выше случае оболочка, которая выходит, не является сценарием, _main | logic | pipelineа ( subshell in which we ${test?} ) ||требует небольшой песочницы.
И это может быть неочевидно, но если вы хотите передать только противоположный случай или только set=значения, это тоже довольно просто:
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испытание. Больше информации в ссылке выше, и вот она снова .
И я думаю, что мы должны показать нашу _testработу функции, верно? Мы просто объявляем 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
echo $( made up name )на$( made up name )производит желаемое поведение. У меня нет объяснения, хотя.