Catatan: zsh
akan mengeluh tentang "pola buruk" jika Anda tidak mengonfigurasinya untuk menerima "komentar sebaris" untuk sebagian besar contoh di sini dan tidak menjalankannya melalui shell proxy seperti yang telah saya lakukan sh <<-\CMD
.
Ok, jadi, seperti yang saya nyatakan dalam komentar di atas, saya tidak tahu secara spesifik tentang bashset -E
, tetapi saya tahu bahwa shell yang kompatibel dengan POSIX menyediakan cara sederhana untuk menguji suatu nilai jika Anda menginginkannya:
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
Di atas Anda akan melihat bahwa meskipun saya dulu masih parameter expansion
menguji${empty?} _test()
return
s pass - seperti yang tampak dalam yang terakhir echo
ini terjadi karena nilai gagal membunuh $( command substitution )
subkulit yang berisi itu, tapi shell induknya - _test
saat ini - terus truk. Dan echo
tidak peduli - itu banyak senang untuk melayani hanya \newline; echo
adalah tidak ujian.
Tapi pertimbangkan ini:
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
Karena saya _test()'s
memasukkan input dengan parameter yang sudah dievaluasi pada fungsi INIT here-document
sekarang _test()
bahkan tidak mencoba untuk berjalan sama sekali. Terlebih lagi sh
cangkang itu tampaknya sepenuhnya menghilangkan hantu dan echo "this doesnt even print"
bahkan tidak mencetak.
Mungkin itu bukan yang Anda inginkan.
Ini terjadi karena ${var?}
perluasan parameter gaya dirancang untuk keluarshell
jika ada parameter yang hilang, itu berfungsi seperti ini :
${parameter:?[word]}
Tunjukkan Kesalahan jika Null
atau Unset.
Jika parameter tidak disetel atau nol, expansion of word
(atau pesan yang menunjukkannya tidak disetel jika kata dihilangkan) akan menjadiwritten to standard error
dan shell exits with a non-zero exit status
. Jika tidak, nilai parameter shall be substituted
. Shell interaktif tidak perlu keluar.
Saya tidak akan menyalin / menempelkan seluruh dokumen, tetapi jika Anda menginginkan kegagalan untuk set but null
nilai, Anda menggunakan formulir:
${var
:? error message }
Dengan :colon
seperti di atas. Jika Anda ingin null
nilai berhasil, cukup abaikan tanda titik dua. Anda juga dapat meniadakannya dan gagal hanya untuk menetapkan nilai, seperti yang akan saya tunjukkan sebentar lagi.
Berlari lagi _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
Ini berfungsi dengan semua jenis tes cepat, tetapi di atas Anda akan melihat bahwa _test()
, jalankan dari tengah yang pipeline
gagal, dan sebenarnya command list
subshell yang mengandungnya gagal seluruhnya, karena tidak ada perintah dalam fungsi yang dijalankan maupun yang berikut ini.echo
berjalan sama sekali, meskipun itu juga menunjukkan bahwa itu dapat dengan mudah diuji karena echo "now it prints"
sekarang dicetak.
Iblis ada dalam perinciannya, saya kira. Dalam kasus di atas, shell yang keluar bukan milik skrip_main | logic | pipeline
tetapi yang ( subshell in which we ${test?} ) ||
disebut sandboxing kecil.
Dan itu mungkin tidak jelas, tetapi jika Anda hanya ingin lulus untuk kasus sebaliknya, atau hanya set=
nilai-nilai, itu cukup sederhana juga:
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
Contoh di atas mengambil keuntungan dari semua 4 bentuk substitusi parameter POSIX dan berbagai :colon null
atau not null
tes mereka. Ada informasi lebih lanjut di tautan di atas, dan ini dia lagi .
Dan saya kira kita harus menunjukkan milik kita _test
fungsi bekerja juga, kan? Kami hanya mendeklarasikan empty=something
sebagai parameter untuk fungsi kami (atau kapan saja sebelumnya):
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!
Perlu dicatat bahwa evaluasi ini berdiri sendiri - tidak memerlukan tes tambahan untuk gagal. Beberapa contoh lagi:
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?
Dan akhirnya kami kembali ke pertanyaan awal: bagaimana menangani kesalahan dalam$(command substitution)
subkulit? Yang benar adalah - ada dua cara, tetapi tidak ada yang langsung. Inti dari masalah adalah proses evaluasi shell - ekspansi shell (termasuk$(command substitution)
) terjadi lebih awal dalam proses evaluasi shell daripada eksekusi perintah shell saat ini - yaitu ketika kesalahan Anda dapat ditangkap dan dijebak.
Masalah yang dialami op adalah pada saat shell saat ini mengevaluasi kesalahan, the $(command substitution)
subshell sudah diganti - tidak ada kesalahan yang tersisa.
Jadi apa dua cara itu? Anda melakukannya secara eksplisit di dalam$(command substitution)
subkulit dengan tes seperti yang Anda lakukan tanpanya, atau Anda menyerap hasilnya ke dalam variabel shell saat ini dan menguji nilainya.
Metode 1:
echo "$(madeup && echo \: || echo '${fail:?die}')" |\
. /dev/stdin
sh: command not found: madeup
/dev/stdin:1: fail: die
echo $?
126
Metode 2:
var="$(madeup)" ; echo "${var:?die} still not stderr"
sh: command not found: madeup
sh: var: die
echo $?
1
Ini akan gagal terlepas dari jumlah variabel yang dinyatakan per baris:
v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"
sh: command not found: madeup
sh: v1: parameter not set
Dan nilai pengembalian kami tetap konstan:
echo $?
1
SEKARANG JEJAK:
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 )
dengan$( made up name )
menghasilkan perilaku yang diinginkan. Saya tidak punya penjelasan.