Menurut panduan ref ini :
-E (juga -o errtrace)
Jika disetel, setiap jebakan pada ERR diwarisi oleh fungsi shell, pergantian perintah, dan perintah yang dijalankan di lingkungan subkulit. Perangkap ERR biasanya tidak diwariskan dalam kasus-kasus seperti itu.
Namun, saya harus menafsirkannya dengan salah, 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 berfungsi seperti kode di atas? Atau, kemungkinan besar, saya salah baca?
bash
command-substitution
dgo.a
sumber
sumber
echo $( made up name )
dengan$( made up name )
menghasilkan perilaku yang diinginkan. Saya tidak punya penjelasan.var=$( pipe )
dan$( pipe )
contoh keduanya akan mewakili titik akhir pipa sedangkanpipe > echo
tidak. Halaman manual saya mengatakan: "1. Kegagalan setiap perintah individu dalam pipa multi-perintah tidak akan menyebabkan shell keluar. Hanya kegagalan pipa itu sendiri yang akan dipertimbangkan."echo
selalu kembali 0. Ini harus diperhitungkan dalam analisis ...Jawaban:
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 lakukansh <<-\CMD
.Ok, jadi, seperti yang saya nyatakan dalam komentar di atas, saya tidak tahu secara spesifik tentang bash
set -E
, tetapi saya tahu bahwa shell yang kompatibel dengan POSIX menyediakan cara sederhana untuk menguji suatu nilai jika Anda menginginkannya:Di atas Anda akan melihat bahwa meskipun saya dulu masih
parameter expansion
menguji${empty?} _test()
return
s pass - seperti yang tampak dalam yang terakhirecho
ini terjadi karena nilai gagal membunuh$( command substitution )
subkulit yang berisi itu, tapi shell induknya -_test
saat ini - terus truk. Danecho
tidak peduli - itu banyak senang untuk melayani hanya\newline; echo
adalah tidak ujian.Tapi pertimbangkan ini:
Karena saya
_test()'s
memasukkan input dengan parameter yang sudah dievaluasi pada fungsiINIT here-document
sekarang_test()
bahkan tidak mencoba untuk berjalan sama sekali. Terlebih lagish
cangkang itu tampaknya sepenuhnya menghilangkan hantu danecho "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 :Saya tidak akan menyalin / menempelkan seluruh dokumen, tetapi jika Anda menginginkan kegagalan untuk
set but null
nilai, Anda menggunakan formulir:Dengan
:colon
seperti di atas. Jika Anda inginnull
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():
Ini berfungsi dengan semua jenis tes cepat, tetapi di atas Anda akan melihat bahwa
_test()
, jalankan dari tengah yangpipeline
gagal, dan sebenarnyacommand 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 karenaecho "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:Contoh di atas mengambil keuntungan dari semua 4 bentuk substitusi parameter POSIX dan berbagai
:colon null
ataunot 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 mendeklarasikanempty=something
sebagai parameter untuk fungsi kami (atau kapan saja sebelumnya):Perlu dicatat bahwa evaluasi ini berdiri sendiri - tidak memerlukan tes tambahan untuk gagal. Beberapa contoh lagi:
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:
Metode 2:
Ini akan gagal terlepas dari jumlah variabel yang dinyatakan per baris:
Dan nilai pengembalian kami tetap konstan:
SEKARANG JEJAK:
sumber
echo "abc" "${v1:?}"
tampaknya tidak mengeksekusi (abc tidak pernah dicetak). Dan shell mengembalikan 1. Ini benar dengan atau tanpa perintah genap ("${v1:?}"
langsung pada cli). Tetapi untuk skrip OP, semua yang diperlukan untuk memicu jebakan adalah menempatkan tugas variabelnya yang berisi substitusi untuk perintah yang tidak ada sendirian pada satu baris. Kalau tidak, perilaku gema adalah untuk mengembalikan 0 selalu, kecuali terganggu seperti dengan tes yang Anda jelaskan.v=$( madeup )
. Saya tidak melihat apa yang tidak aman dengan itu. Itu hanya tugas, orang itu salah mengeja perintah misalnyav="$(lss)"
. Itu kesalahan. Ya Anda dapat memverifikasi dengan status kesalahan dari perintah terakhir $? - karena itu adalah perintah di telepon (penugasan tanpa nama perintah) dan tidak ada yang lain - bukan argumen untuk menggemakan. Plus, di sini ia terjebak oleh fungsi as! = 0, jadi Anda mendapat umpan balik dua kali. Kalau tidak pasti seperti yang Anda jelaskan ada cara yang lebih baik untuk melakukan ini secara tertib dalam suatu kerangka kerja tetapi OP memiliki 1 baris: gema plus substitusi yang gagal. Dia bertanya-tanya tentang gema.Dalam skrip Anda, ini adalah eksekusi perintah (
echo $( made up name )
). Dalam perintah bash dibatasi dengan baik ; atau dengan baris baru . Di perintah$( made up name )
dianggap sebagai bagian dari perintah. Bahkan jika bagian ini gagal dan kembali dengan kesalahan, seluruh perintah dijalankan dengan sukses karenaecho
tidak tahu tentang hal itu. Ketika perintah kembali dengan 0 no trap dipicu.Anda harus memasukkannya ke dalam dua perintah, penugasan, dan gema
sumber
echo $var
tidak gagal -$var
diperluas menjadi kosong sebelumecho
benar-benar melihatnya.Ini karena bug di bash. Selama Pergantian Komando langkah dalam
made
menjalankan (atau gagal ditemukan) dalam sebuah subkulit, tetapi subkulit tersebut "dioptimalkan" sedemikian rupa sehingga tidak menggunakan beberapa perangkap dari shell induk. Ini diperbaiki dalam versi 4.4.5 :Dengan bash 4.4.5 atau lebih tinggi, Anda akan melihat output berikut:
Penangan perangkap telah dipanggil seperti yang diharapkan, kemudian subkulit keluar. (
set -e
hanya menyebabkan subkulit keluar, bukan induknya, sehingga pesan "tidak boleh dihubungi" seharusnya, pada kenyataannya, tercapai.)Solusi untuk versi yang lebih lama adalah untuk memaksa pembuatan subkulit penuh yang tidak dioptimalkan:
Ruang tambahan diperlukan untuk membedakan dari Ekspansi Aritmatika.
sumber