Примітка: zshскаржитеся на "погані шаблони", якщо ви не налаштовуєте його на прийняття "вбудованих коментарів" для більшості прикладів тут і не запускайте їх через проксі-сервер, як я це робив sh <<-\CMD.
Так, як я вже говорив у коментарях вище, я не знаю конкретно про bash'sset -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тестував пропуск - як це доводиться в останньому. 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?}розширення параметрів стилю призначене для виходу з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помилок, а насправді його вміст 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 )виробляє бажану поведінку. Однак у мене немає пояснень.