Примечание: 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()
еще return
S проход - как в проявили последнее 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 )
производит желаемое поведение. У меня нет объяснения, хотя.