Ada beberapa cara lagi untuk mendekati masalah ini. Dengan asumsi salah satu kebutuhan Anda adalah menjalankan skrip/fungsi shell yang berisi beberapa perintah shell dan memeriksa apakah skrip berjalan dengan sukses dan membuang kesalahan jika terjadi kegagalan.
Perintah shell pada umumnya mengandalkan kode keluar yang dikembalikan untuk memberi tahu shell jika berhasil atau gagal karena beberapa kejadian yang tidak terduga.
Jadi apa yang ingin Anda lakukan termasuk dalam dua kategori ini
- keluar jika ada kesalahan
- keluar dan bersihkan kesalahan
Bergantung pada yang mana yang ingin Anda lakukan, ada opsi shell yang tersedia untuk digunakan. Untuk kasus pertama, shell menyediakan opsi dengan set -e
dan untuk yang kedua Anda bisa melakukan trap
pada EXIT
Haruskah saya menggunakan exit
dalam skrip/fungsi saya?
Menggunakan exit
umumnya meningkatkan keterbacaan Dalam rutinitas tertentu, setelah Anda mengetahui jawabannya, Anda ingin segera keluar ke rutinitas pemanggilan. Jika rutin didefinisikan sedemikian rupa sehingga tidak memerlukan pembersihan lebih lanjut setelah mendeteksi kesalahan, tidak segera keluar berarti Anda harus menulis lebih banyak kode.
Jadi jika Anda perlu melakukan tindakan pembersihan pada skrip untuk membuat penghentian skrip menjadi bersih, sebaiknya tidak untuk menggunakan exit
.
Haruskah saya menggunakan set -e
untuk kesalahan saat keluar?
Tidak!
set -e
adalah upaya untuk menambahkan "deteksi kesalahan otomatis" ke shell. Tujuannya adalah untuk menyebabkan shell dibatalkan setiap kali terjadi kesalahan, tetapi ia datang dengan banyak potensi jebakan misalnya,
-
Perintah yang merupakan bagian dari tes if kebal. Dalam contoh, jika Anda mengharapkannya rusak pada
test
periksa direktori yang tidak ada, itu tidak akan, itu menuju ke kondisi lainset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
Perintah dalam pipa selain yang terakhir, kebal. Pada contoh di bawah ini, karena kode keluar perintah yang terakhir dieksekusi (paling kanan) dianggap (
cat
) dan berhasil. Hal ini dapat dihindari dengan menyetelnya denganset -o pipefail
opsi tetapi masih peringatan.set -e somecommand that fails | cat - echo survived
Direkomendasikan untuk digunakan - trap
saat keluar
Putusannya adalah jika Anda ingin dapat menangani kesalahan alih-alih keluar secara membabi buta, alih-alih menggunakan set -e
, gunakan trap
pada ERR
sinyal semu.
ERR
trap bukan untuk menjalankan kode ketika shell itu sendiri keluar dengan kode kesalahan bukan nol, tetapi ketika ada perintah yang dijalankan oleh shell itu yang bukan bagian dari suatu kondisi (seperti di if cmd
, atau cmd ||
) keluar dengan status keluar bukan nol.
Praktik umum adalah kami mendefinisikan penangan jebakan untuk memberikan informasi debug tambahan di baris mana dan apa yang menyebabkan keluarnya. Ingat kode keluar dari perintah terakhir yang menyebabkan ERR
sinyal akan tetap tersedia pada saat ini.
cleanup() {
exitcode=$?
printf 'error condition hit\n' 1>&2
printf 'exit code returned: %s\n' "$exitcode"
printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
dan kami hanya menggunakan penangan ini seperti di bawah ini di atas skrip yang gagal
trap cleanup ERR
Menyatukan ini pada skrip sederhana yang berisi false
pada baris 15, informasi yang akan Anda dapatkan sebagai
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
trap
juga menyediakan opsi terlepas dari kesalahan untuk hanya menjalankan pembersihan pada penyelesaian shell (misalnya skrip shell Anda keluar), pada sinyal EXIT
. Anda juga bisa menjebak beberapa sinyal sekaligus. Daftar sinyal yang didukung untuk dijebak dapat ditemukan di trap.1p - halaman manual Linux
Hal lain yang perlu diperhatikan adalah memahami bahwa tidak ada metode yang disediakan berfungsi jika Anda berurusan dengan sub-shell yang terlibat dalam hal ini, Anda mungkin perlu menambahkan penanganan kesalahan Anda sendiri.
-
Pada sub-kulit dengan
set -e
tidak akan berhasil.false
terbatas pada sub-shell dan tidak pernah disebarkan ke shell induk. Untuk melakukan penanganan error di sini, tambahkan logika Anda sendiri untuk melakukan(false) || false
set -e (false) echo survived
-
Hal yang sama terjadi dengan
trap
juga. Logika di bawah ini tidak akan berfungsi karena alasan yang disebutkan di atas.trap 'echo error' ERR (false)
Penanganan error dasar
Jika runner kasus pengujian mengembalikan kode bukan nol untuk pengujian yang gagal, Anda cukup menulis:
test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
printf '%s\n' "Test case x failed" >&2 # write error message to stderr
exit 1 # or exit $test_result
fi
Atau bahkan lebih singkat:
if ! test_handler test_case_x; then
printf '%s\n' "Test case x failed" >&2
exit 1
fi
Atau yang terpendek:
test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
Untuk keluar dengan kode keluar test_handler:
test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
Penanganan error lanjutan
Jika Anda ingin mengambil pendekatan yang lebih komprehensif, Anda dapat memiliki penangan kesalahan:
exit_if_error() {
local exit_code=$1
shift
[[ $exit_code ]] && # do nothing if no error code passed
((exit_code != 0)) && { # do nothing if error code is 0
printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
kemudian aktifkan setelah menjalankan test case Anda:
run_test_case test_case_x
exit_if_error $? "Test case x failed"
atau
run_test_case test_case_x || exit_if_error $? "Test case x failed"
Keuntungan memiliki penangan kesalahan seperti exit_if_error
adalah:
- kami dapat membakukan semua logika penanganan error seperti logging, mencetak pelacakan tumpukan, notifikasi, melakukan pembersihan, dll., di satu tempat
- dengan membuat penangan kesalahan mendapatkan kode kesalahan sebagai argumen, kita dapat menghindarkan penelepon dari kekacauan
if
memblokir yang menguji kode keluar untuk kesalahan - jika kita memiliki penangan sinyal (menggunakan trap), kita dapat memanggil penangan kesalahan dari sana
Penanganan kesalahan dan pustaka logging
Berikut adalah implementasi lengkap dari penanganan kesalahan dan logging:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Postingan terkait
- Penanganan kesalahan di Bash
- Perintah bawaan 'penelepon' di Bash Hackers Wiki
- Apakah ada kode status keluar standar di Linux?
- BashFAQ/105 - Mengapa set -e (atau set -o errexit, atau trap ERR) tidak melakukan apa yang saya harapkan?
- Setara dengan
__FILE__
,__LINE__
di Bash - Apakah ada perintah TRY CATCH di Bash
- Untuk menambahkan pelacakan tumpukan ke penangan kesalahan, Anda mungkin ingin melihat posting ini:Jejak program yang dijalankan yang dipanggil oleh skrip Bash
- Mengabaikan kesalahan tertentu dalam skrip shell
- Menangkap kode kesalahan dalam pipa shell
- Bagaimana cara mengelola verbositas log di dalam skrip shell?
- Bagaimana cara mencatat nama fungsi dan nomor baris di Bash?
- Apakah tanda kurung siku ganda [[ ]] lebih disukai daripada tanda kurung siku tunggal [ ] di Bash?
Ini tergantung di mana Anda ingin pesan kesalahan disimpan.
Anda dapat melakukan hal berikut:
echo "Error!" > logfile.log
exit 125
Atau yang berikut ini:
echo "Error!" 1>&2
exit 64
Saat Anda mengajukan pengecualian, Anda menghentikan eksekusi program.
Anda juga dapat menggunakan sesuatu seperti exit xxx
di mana xxx
adalah kode kesalahan yang mungkin ingin Anda kembalikan ke sistem operasi (dari 0 hingga 255). Ini 125
dan 64
hanyalah kode acak yang dapat Anda gunakan untuk keluar. Saat Anda perlu memberi tahu OS bahwa program berhenti secara tidak normal (mis. terjadi kesalahan), Anda harus memberikan kode keluar bukan nol ke exit
.
Seperti yang ditunjukkan oleh @chepner, Anda dapat melakukan exit 1
, yang berarti kesalahan yang tidak ditentukan .