Bagaimana membuat bash membatalkan eksekusi skrip pada kesalahan sintaksis?

15

Agar aman, saya ingin bash membatalkan eksekusi skrip jika menemui kesalahan sintaksis.

Yang mengejutkan saya, saya tidak bisa mencapai ini. ( set -etidak cukup.) Contoh:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Hasil (bash-3.2.39 atau bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Ya, kami tidak dapat memeriksa $?setelah setiap pernyataan untuk mengetahui kesalahan sintaksis.

(Saya mengharapkan perilaku aman dari bahasa pemrograman yang masuk akal ... mungkin ini harus dilaporkan sebagai bug / keinginan untuk mem-bash pengembang)

Eksperimen lebih banyak

if tidak ada bedanya.

Menghapus if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Hasil:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Mungkin, ini terkait dengan latihan 2 dari http://mywiki.wooledge.org/BashFAQ/105 dan ada hubungannya dengan (( )). Tapi saya merasa masih tidak masuk akal untuk terus mengeksekusi kesalahan sintaks.

Tidak, (( ))tidak ada bedanya!

Itu berperilaku buruk bahkan tanpa tes aritmatika! Hanya skrip dasar yang sederhana:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Hasil:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 
imz - Ivan Zakharyaschev
sumber
set -etidak cukup karena kesalahan sintaks Anda dalam ifpernyataan. Di tempat lain harus batalkan naskah.
jordanm
@jordanm Ok, ini bisa menjadi penjelasan mengapa set -etidak berhasil. Tapi pertanyaan saya masih masuk akal. Apakah mungkin untuk membatalkan kesalahan sintaks?
imz - Ivan Zakharyaschev
@jordanm Dihapus "jika"; tidak membuat perbedaan (memperbarui pertanyaan saya).
imz - Ivan Zakharyaschev

Jawaban:

9

Membungkus keseluruhan menjadi suatu fungsi tampaknya melakukan trik:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Hasil:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Meskipun saya tidak tahu mengapa - mungkin orang lain bisa menjelaskan?

ahilsend
sumber
2
Sekarang definisi fungsi Anda diuraikan dan dievaluasi, dan gagal.
tripleee
Solusi bagus! BTW, itu tidak membatalkan seluruh program dalam kasus ini juga. Saya telah menambahkan echo 'Bad2: has not aborted the execution after bad main!'sebagai yang terakhir pada contoh Anda, dan hasilnya adalah: $ LC_ALL = C./sh-on-syntax-err ./sh-on-syntax-err: baris 6: #: kesalahan sintaks: operan diharapkan (operand diharapkan) token kesalahan adalah "#") Bad2: belum membatalkan eksekusi setelah main buruk! $
imz - Ivan Zakharyaschev
Tapi kita seharusnya tidak menambahkan satu baris saja, kita harus meletakkan segala sesuatu di dalam suatu fungsi.
imz - Ivan Zakharyaschev
@ tripleee Ya, sepertinya parsing fungsi gagal, jadi tidak lengkap, tetapi keseluruhan program tidak dibatalkan sebenarnya dalam kasus ini (jadi itu bukan efek dari exit-on-error, mungkin).
imz - Ivan Zakharyaschev
6

Anda mungkin menyesatkan tentang arti asli dari set -e. Pembacaan yang cermat dari output help setacara:

-e  Exit immediately if a command exits with a non-zero status.

Begitu -ejuga tentang status keluar dari perintah yang bukan nol, bukan tentang kesalahan sintaksis dalam skrip Anda.

Secara umum, ini dianggap praktik buruk untuk digunakan set -e, karena semua kesalahan (yaitu, semua pengembalian non-nol dari perintah) harus ditangani dengan cerdas oleh skrip (pikirkan skrip yang kuat, bukan skrip yang menjadi liar setelah Anda memasukkan nama file dengan ruang atau yang dimulai dengan tanda hubung).

Bergantung pada jenis kesalahan sintaksis, skrip mungkin bahkan tidak dieksekusi sama sekali. Saya tidak cukup berpengetahuan di bash untuk mengetahui dengan tepat kelas kesalahan sintaksis apa (yang dapat diklasifikasikan) yang dapat menyebabkan aborsi skrip dengan segera atau tidak. Mungkin beberapa guru Bash akan bergabung dan menjelaskan semuanya.

Saya hanya berharap saya mengklarifikasi set -epernyataan itu!

Tentang keinginan Anda:

Saya mengharapkan perilaku aman dari bahasa pemrograman yang masuk akal ... mungkin ini harus dilaporkan sebagai bug / keinginan untuk mem-bash pengembang

Jawabannya jelas tidak! seperti yang telah Anda amati ( set -etidak merespons seperti yang Anda harapkan) sebenarnya didokumentasikan dengan sangat baik.

gniourf_gniourf
sumber
Maksud saya tidak adanya fitur tersebut merupakan masalah. Saya tidak ingin fokus set -e- hanya sedikit dekat dengan tujuan saya, itu sebabnya disebutkan dan digunakan di sini. Pertanyaan saya bukan tentang set -e, ini tentang ketidakamanan bash jika tidak dapat dibuat untuk membatalkan kesalahan sytax. Saya mencari cara untuk membuatnya selalu gagal pada kesalahan sintaksis.
imz - Ivan Zakharyaschev
4

Anda dapat membuat skrip memeriksa sendiri dengan meletakkan sesuatu seperti

bash -n "$0"

di dekat bagian atas skrip - setelah set -etetapi sebelum potongan kode yang signifikan.

Saya harus mengatakan ini tidak terasa sangat kuat, tetapi jika itu bekerja untuk Anda, mungkin itu dapat diterima.

tripleee
sumber
Luar biasa! Atau, tanpa set -e: bash -n "$0" || exit
Daniel S
0

Pertama, (( )) in bash digunakan sebagai perhitungan aritmatika, bukan untuk digunakan dalam if ... gunakan []untuk itu.

Kedua, itu ${a[#]} aneh dan itulah sebabnya memberikan kesalahan ... #tidak memiliki arti array

Saya tidak tahu apa yang ingin Anda lakukan dengan ini, tetapi saya berasumsi Anda ingin tahu jumlah bidang, jadi Anda mau ${#a[*]} gantinya

Akhirnya, ketika membandingkan bilangan bulat, -eqdirekomendasikan lebih ==(digunakan untuk string). itu== juga akan bekerja, tetapi -eqdianjurkan.

jadi kamu mau:

if [ ${#a[*]} -eq 2 ]; then 
higuita
sumber
4
Itu tidak benar. Sudah biasa menggunakan ((kata kunci dengan ifkata kunci. Sebagai contoh if (( 5 * $b > 53 )),. Kecuali Anda bertujuan untuk portabilitas dengan kerang yang lebih tua, [[adalah umumnya lebih lebih [.
2
Ya, saya setuju dengan @van - [[dan ((dirancang khusus sebagai tes ringan untuk digunakan dengan "jika" dll - tidak seperti [, mereka tidak pernah menelurkan subproses untuk mengevaluasi kondisinya.
imz - Ivan Zakharyaschev
1
[adalah bash builtin. Kerang yang lebih tua mengharapkannya menjadi programnya sendiri.