Примітка: 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 )
виробляє бажану поведінку. Однак у мене немає пояснень.