GNU/Linux >> Belajar Linux >  >> Linux

Menjebak Kesalahan Dalam Substitusi Perintah Menggunakan “-o Errtrace” (yaitu Set -e)?

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 atau Unset. Jika parameter tidak disetel atau nol, expansion of word (atau pesan yang menunjukkan itu tidak disetel jika kata dihilangkan) harus written 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 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.

Terkait:Tidak dapat membuka aplikasi sebagai root. Perintah Sudo tidak ditemukan?

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.

Terkait:Pernyataan Pilih PostgreSQL menghasilkan kesalahan dalam datagrip tetapi tidak pgAdmin4?

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

Linux
  1. Pergantian Perintah:Memisahkan Pada Baris Baru Tapi Bukan Spasi?

  2. Substitusi Perintah Menggunakan “?

  3. Menggunakan Ekspor Di .bashrc?

  1. Ubah Font Di Perintah Gema?

  2. Perintah gema Linux

  3. Bagaimana cara mengatur Tugas Otomatis pada VPS Linux Menggunakan Cron?

  1. Proksi TCP menggunakan socat

  2. 10 Contoh Praktis Menggunakan Perintah scp

  3. Bagaimana cara mengatur variabel lingkungan dalam GDB menggunakan perintah Shell?