Apa metode favorit Anda untuk menangani kesalahan di Bash? Contoh terbaik penanganan kesalahan yang saya temukan di web ditulis oleh William Shotts, Jr di http://www.linuxcommand.org .
Dia menyarankan menggunakan fungsi berikut untuk penanganan kesalahan di Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
Apakah Anda memiliki rutinitas penanganan kesalahan yang lebih baik yang Anda gunakan dalam skrip Bash?
Jawaban:
Gunakan perangkap!
... lalu, setiap kali Anda membuat file sementara:
dan
$temp_foo
akan dihapus saat keluar, dan nomor baris saat ini akan dicetak. (set -e
juga akan memberi Anda perilaku exit-on-error, meskipun disertai dengan peringatan serius dan memperlemah prediktabilitas dan portabilitas kode).Anda dapat membiarkan trap memanggil
error
Anda (dalam hal ini ia menggunakan kode keluar default 1 dan tidak ada pesan) atau menyebutnya sendiri dan memberikan nilai eksplisit; misalnya:akan keluar dengan status 2, dan memberikan pesan eksplisit.
sumber
Itu solusi yang bagus. Saya hanya ingin menambahkan
sebagai mekanisme kesalahan yang belum sempurna. Ini akan segera menghentikan skrip Anda jika perintah sederhana gagal. Saya pikir ini seharusnya menjadi perilaku default: karena kesalahan seperti itu hampir selalu menandakan sesuatu yang tidak terduga, itu tidak benar-benar 'waras' untuk terus menjalankan perintah berikut.
sumber
set -e
bukan tanpa Gotcha : Lihat mywiki.wooledge.org/BashFAQ/105 untuk beberapaset -o pipefail
set -e
memiliki rasio biaya-manfaat yang tinggi.set -e
diri saya sendiri, tetapi sejumlah pelanggan tetap lainnya di irc.freenode.org # bash menyarankan (dalam istilah yang cukup kuat) menentangnya. Minimal, para gotcha yang dimaksud harus dipahami dengan baik.Membaca semua jawaban di halaman ini sangat menginspirasi saya.
Jadi, inilah petunjuk saya:
konten file: lib.trap.sh
Contoh penggunaan:
konten file: trap-test.sh
Berlari:
Keluaran:
Seperti yang Anda lihat dari tangkapan layar di bawah ini, outputnya berwarna dan pesan kesalahannya muncul dalam bahasa yang digunakan.
sumber
test ${#g_libs[@]} == 0
tidak kompatibel dengan POSIX (uji POSIX mendukung=
perbandingan string atau-eq
untuk perbandingan numerik, tetapi tidak==
, belum lagi kurangnya array dalam POSIX), dan jika Anda tidak mencoba untuk menjadi kompatibel dengan POSIX, mengapa di dunia yang Anda gunakantest
sama sekali alih-alih konteks matematika?(( ${#g_libs[@]} == 0 ))
bagaimanapun, lebih mudah dibaca.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Alternatif yang setara dengan "set -e" adalah
Itu membuat arti bendera agak lebih jelas daripada hanya "-e".
Tambahan acak: untuk menonaktifkan sementara flag, dan kembali ke default (melanjutkan eksekusi terlepas dari kode keluar), cukup gunakan
Ini mencegah penanganan kesalahan yang tepat yang disebutkan dalam respons lain, tetapi cepat & efektif (seperti halnya bash).
sumber
$(foo)
pada garis telanjang daripada hanyafoo
biasanya Hal yang Salah. Mengapa mempromosikannya dengan memberikannya sebagai contoh?Terinspirasi oleh ide-ide yang disajikan di sini, saya telah mengembangkan cara yang mudah dibaca dan nyaman untuk menangani kesalahan dalam skrip bash dalam proyek bash boilerplate saya .
Dengan hanya sumber pustaka, Anda mendapatkan berikut ini dari kotak (yaitu akan menghentikan eksekusi pada setiap kesalahan, seolah-olah menggunakan
set -e
berkattrap
onERR
dan beberapa bash-fu ):Ada beberapa fitur tambahan yang membantu menangani kesalahan, seperti coba dan tangkap , atau lemparan kata kunci , yang memungkinkan Anda untuk memutuskan eksekusi pada suatu titik untuk melihat jejak balik. Plus, jika terminal mendukungnya, ia mengeluarkan emoji powerline, mewarnai bagian-bagian output agar mudah dibaca, dan menggarisbawahi metode yang menyebabkan pengecualian dalam konteks garis kode.
Kelemahannya adalah - ini tidak portabel - kode bekerja di bash, mungkin> = 4 saja (tapi saya bayangkan itu bisa diangkut dengan upaya untuk bash 3).
Kode dipisahkan menjadi beberapa file untuk penanganan yang lebih baik, tetapi saya terinspirasi oleh ide backtrace dari jawaban di atas oleh Luca Borrione .
Untuk membaca lebih lanjut atau melihat sumbernya, lihat GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
sumber
Saya lebih suka sesuatu yang sangat mudah dipanggil. Jadi saya menggunakan sesuatu yang terlihat sedikit rumit, tetapi mudah digunakan. Saya biasanya hanya menyalin dan menempelkan kode di bawah ini ke skrip saya. Penjelasan mengikuti kode.
Saya biasanya melakukan panggilan ke fungsi pembersihan di samping fungsi error_exit, tetapi ini bervariasi dari satu skrip ke skrip yang lain sehingga saya mengabaikannya. Perangkap menangkap sinyal terminasi umum dan memastikan semuanya dibersihkan. Alias adalah apa yang melakukan sihir yang sebenarnya. Saya suka memeriksa semuanya untuk kegagalan. Jadi secara umum saya memanggil program dalam "jika!" ketikkan pernyataan. Dengan mengurangi 1 dari nomor baris alias akan memberi tahu saya di mana kegagalan terjadi. Itu juga sangat sederhana untuk dipanggil, dan cukup banyak bukti bodoh. Di bawah ini adalah contoh (cukup ganti / bin / false dengan apa pun yang akan Anda panggil).
sumber
$LINENO - 1
. Tunjukkan dengan benar tanpa itu.false || die "hello death"
Pertimbangan lain adalah kode keluar untuk kembali. Hanya "
1
" cukup standar, meskipun ada beberapa kode keluar khusus yang digunakan bash sendiri , dan halaman yang sama berpendapat bahwa kode yang ditentukan pengguna harus dalam kisaran 64-113 untuk memenuhi standar C / C ++.Anda mungkin juga mempertimbangkan pendekatan vektor bit yang
mount
digunakan untuk kode keluarnya:OR
-menyatukan kode memungkinkan skrip Anda memberi sinyal beberapa kesalahan simultan.sumber
Saya menggunakan kode jebakan berikut, ini juga memungkinkan kesalahan untuk dilacak melalui pipa dan perintah 'waktu'
sumber
function
kunci ini tidak kompatibel dengan POSIX. Pertimbangkan untuk membuat pernyataan Anda dengan adilerror() {
, tanpafunction
sebelumnya.${$?}
seharusnya$?
, atau${?}
jika Anda bersikeras menggunakan kawat gigi yang tidak perlu; batin$
salah.Saya sudah menggunakan
sebelum; Saya pikir karena 'exit' gagal untuk saya karena suatu alasan. Default di atas sepertinya ide yang bagus.
sumber
Ini telah membantu saya dengan baik untuk sementara waktu sekarang. Ini mencetak pesan kesalahan atau peringatan dengan warna merah, satu baris per parameter, dan memungkinkan kode keluar opsional.
sumber
Tidak yakin apakah ini akan membantu Anda, tetapi saya memodifikasi beberapa fungsi yang disarankan di sini untuk memasukkan pemeriksaan kesalahan (kode keluar dari perintah sebelumnya) di dalamnya. Pada setiap "periksa", saya juga memberikan "pesan" sebagai parameter tentang kesalahan tersebut untuk tujuan pencatatan.
Sekarang untuk memanggilnya dalam skrip yang sama (atau yang lain jika saya gunakan
export -f error_exit
) Saya cukup menulis nama fungsi dan meneruskan pesan sebagai parameter, seperti ini:Dengan menggunakan ini saya dapat membuat file bash yang sangat kuat untuk beberapa proses otomatis dan itu akan berhenti jika terjadi kesalahan dan memberi tahu saya (
log.sh
akan melakukannya)sumber
function
kata kunci, adilerror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Trik ini berguna untuk perintah atau fungsi yang hilang. Nama fungsi yang hilang (atau dapat dieksekusi) akan diberikan dalam $ _
sumber
$_
fungsi yang tersedia sama$?
? Saya tidak yakin ada alasan untuk menggunakan salah satu fungsi tetapi tidak yang lain.Fungsi ini telah melayani saya dengan cukup baik baru-baru ini:
Anda menyebutnya dengan menambahkan 0 atau nilai pengembalian terakhir ke nama perintah yang akan dijalankan, sehingga Anda dapat mengaitkan perintah tanpa harus memeriksa nilai kesalahan. Dengan ini, pernyataan ini memblokir:
Menjadi ini:
Jika salah satu perintah gagal, kode kesalahan hanya diteruskan ke ujung blok. Saya merasa berguna ketika Anda tidak ingin perintah berikutnya dieksekusi jika yang sebelumnya gagal, tetapi Anda juga tidak ingin skrip keluar langsung (misalnya, di dalam loop).
sumber
Menggunakan perangkap tidak selalu merupakan opsi. Misalnya, jika Anda sedang menulis beberapa jenis fungsi yang dapat digunakan kembali yang memerlukan penanganan kesalahan dan yang dapat dipanggil dari skrip apa pun (setelah sumber file dengan fungsi pembantu), fungsi itu tidak dapat mengasumsikan apa pun tentang waktu keluar skrip luar, yang membuat menggunakan perangkap sangat sulit. Kerugian lain dari menggunakan jebakan adalah kompabilitas yang buruk, karena Anda berisiko menimpa jebakan sebelumnya yang mungkin ditetapkan sebelumnya dalam rantai penelepon.
Ada sedikit trik yang dapat digunakan untuk melakukan penanganan kesalahan yang tepat tanpa jebakan. Seperti yang mungkin sudah Anda ketahui dari jawaban lain,
set -e
tidak berfungsi di dalam perintah jika Anda menggunakan||
operator setelahnya, bahkan jika Anda menjalankannya dalam subkulit; mis. ini tidak akan berfungsi:Tetapi
||
operator diperlukan untuk mencegah kembali dari fungsi luar sebelum pembersihan. Caranya adalah dengan menjalankan perintah dalam di latar belakang, dan kemudian segera tunggu. Thewait
builtin akan mengembalikan kode keluar dari perintah batin, dan sekarang Anda menggunakan||
setelahwait
, bukan fungsi batin, jadiset -e
bekerja dengan baik dalam kedua:Inilah fungsi generik yang dibangun di atas gagasan ini. Ini harus bekerja di semua shell yang kompatibel dengan POSIX jika Anda menghapus
local
kata kunci, yaitu ganti semualocal x=y
hanya denganx=y
:Contoh penggunaan:
Menjalankan contoh:
Satu-satunya hal yang perlu Anda perhatikan ketika menggunakan metode ini adalah bahwa semua modifikasi variabel Shell yang dilakukan dari perintah yang Anda lewati
run
tidak akan merambat ke fungsi panggilan, karena perintah berjalan dalam subkulit.sumber