Menurut manual referensi ini:
-E (juga -o errtrace)
Jika disetel, setiap jebakan pada ERR diwarisi oleh fungsi shell, substitusi perintah, dan perintah yang dijalankan di lingkungan subkulit.
Perangkap ERR biasanya tidak diwariskan dalam kasus seperti itu.
Namun, saya pasti salah mengartikannya, karena yang berikut ini tidak berfungsi:
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
Saya sudah tahu tugas sederhana, my_var=$(made_up_name)
, akan keluar dari skrip dengan set -e
(yaitu errexit).
Apakah -E/-o errtrace
seharusnya bekerja seperti kode di atas? Atau, kemungkinan besar, saya salah membacanya?
Jawaban yang Diterima:
Catatan:zsh
akan mengeluh tentang "pola buruk" jika Anda tidak mengonfigurasinya untuk menerima "komentar sebaris" untuk sebagian besar contoh di sini dan jangan menjalankannya melalui shell proxy seperti yang telah saya lakukan dengan sh <<-CMD
.
Ok, jadi, seperti yang saya nyatakan di komentar di atas, saya tidak tahu secara spesifik tentang bash's set -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 menggunakan parameter expansion
untuk menguji ${empty?} _test()
masih return
s pass – seperti yang ditunjukkan dalam echo
last terakhir Ini terjadi karena nilai yang gagal mematikan $( command substitution )
subkulit yang berisi itu, tetapi cangkang induknya – _test
saat ini – terus menggunakan truk. Dan echo
tidak peduli – sangat senang melayani hanya newline; echo
adalah bukan 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 memberi makan _test()'s
masukan dengan parameter yang telah dievaluasi sebelumnya di INIT here-document
sekarang _test()'s
fungsi bahkan tidak mencoba untuk berjalan sama sekali. Terlebih lagi sh
shell tampaknya menyerahkan hantu sepenuhnya dan echo "this doesnt even print"
bahkan tidak mencetak.
Mungkin itu bukan apa yang Anda inginkan.
Ini terjadi karena ${var?}
style parameter-expansion dirancang untuk keluar dari shell
jika ada parameter yang hilang, ini berfungsi seperti ini:
${parameter:?[word]}
Tunjukkan Kesalahan jika
Null
atauUnset.
Jika parameter tidak disetel atau nol,expansion of word
(atau pesan yang menunjukkan itu tidak disetel jika kata dihilangkan) haruswritten to standard error
danshell exits with a non-zero exit status
. Jika tidak, nilaiparameter 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 yang Anda gunakan formulir:
${var
😕error message }
Dengan :colon
seperti di atas. Jika Anda menginginkan null
nilai untuk berhasil, hilangkan saja titik dua. Anda juga dapat meniadakannya dan gagal hanya untuk nilai yang ditetapkan, seperti yang akan saya tunjukkan sebentar lagi.
Jalankan 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 pengujian cepat, tetapi di atas Anda akan melihat bahwa _test()'s
, jalankan dari tengah pipeline
gagal, dan ternyata berisi command list
subkulit gagal sepenuhnya, karena tidak ada perintah dalam fungsi yang dijalankan atau echo
following berikut dijalankan sama sekali, meskipun juga ditunjukkan bahwa itu dapat dengan mudah diuji karena echo "now it prints"
sekarang dicetak.
Iblis ada dalam detailnya, kurasa. Dalam kasus di atas, shell yang keluar adalah bukan skrip _main | logic | pipeline
tetapi ( subshell in which we ${test?} ) ||
jadi diperlukan sedikit sandbox.
Dan itu mungkin tidak jelas, tetapi jika Anda hanya ingin meneruskan untuk kasus sebaliknya, atau hanya set=
nilai, ini juga cukup sederhana:
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 memanfaatkan semua 4 bentuk substitusi parameter POSIX dan berbagai :colon null
atau not null
tes. Ada informasi lebih lanjut di tautan di atas, dan ini dia lagi.
Dan saya rasa kita harus menunjukkan _test
. kita fungsi kerja 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 -- "[email protected]" #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 kita kembali ke pertanyaan awal :bagaimana menangani kesalahan dalam $(command substitution)
subkulit? Yang benar adalah – ada dua cara, tetapi tidak ada yang langsung. Inti masalahnya adalah proses evaluasi shell – ekspansi shell (termasuk $(command substitution)
) terjadi lebih awal dalam proses evaluasi shell daripada eksekusi perintah shell saat ini – yaitu saat kesalahan Anda dapat ditangkap dan dijebak.
Masalah yang dialami op adalah pada saat shell saat ini mengevaluasi kesalahan, $(command substitution)
subkulit telah diganti – tidak ada kesalahan yang tersisa.
Lalu apa saja dua cara tersebut? Entah 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 dideklarasikan 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 PERANGKATNYA:
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