GNU/Linux >> Belajar Linux >  >> Linux

Pelajari penanganan kesalahan Bash dengan contoh

Dalam artikel ini, saya menyajikan beberapa trik untuk menangani kondisi kesalahan—Beberapa benar-benar tidak termasuk dalam kategori penanganan kesalahan (cara reaktif untuk menangani hal yang tidak terduga) tetapi juga beberapa teknik untuk menghindari kesalahan sebelum terjadi.

Studi kasus:Skrip sederhana yang mengunduh laporan perangkat keras dari beberapa host dan memasukkannya ke dalam database.

Katakanlah Anda memiliki cron pekerjaan di setiap sistem Linux Anda, dan Anda memiliki skrip untuk mengumpulkan informasi perangkat keras dari masing-masing sistem:

#!/bin/bash# Skrip untuk mengumpulkan status keluaran lshw dari server rumah# Ketergantungan:# * LSHW:http://ezix.org/project/wiki/HardwareLiSter# * JQ:http://stedolan.github.io/jq/## Di setiap mesin Anda dapat menjalankan sesuatu seperti ini dari cron (Tidak tahu CRON, jangan khawatir:https://crontab-generator.org/)# 0 0 * * * /usr/sbin/lshw -json -quiet> /var/log/lshw-dump.json# Penulis:Jose Vicente Nunez#declare -a server=(dmaf5)DATADIR="$HOME/Documents/lshw-dump"/usr /bin/mkdir -p -v "$DATADIR"untuk server di ${servers[*]}; do    echo "Mengunjungi:$server"    /usr/bin/scp -o logLevel=Error ${server}:/var/log/lshw-dump.json ${DATADIR}/lshw-$server-dump.json &donewaitfor lshw in $(/usr/bin/find $DATADIR -type f -name 'lshw-*-dump.json'); lakukan    /usr/bin/jq '.["product","vendor", "configuration"]' $lshwdone 

Jika semuanya berjalan dengan baik, Anda mengumpulkan file secara paralel karena Anda tidak memiliki lebih dari sepuluh sistem. Anda dapat melakukan ssh ke semuanya secara bersamaan dan kemudian menampilkan detail perangkat keras masing-masing.

Kunjungan:DMAF5LSHW-DUMP.json 100% 54KB 136.9MB/S 00:00 "DMAF5 (String Default)" "Besstar Tech Limited" {"Boot":"Normal", "Chassis":"Desktop ",  "family":"String default",  "sku":"String default",  "uuid":"00020003-0004-0005-0006-000700080009"}

Berikut adalah beberapa kemungkinan mengapa terjadi kesalahan:

  • Laporan Anda tidak berjalan karena server sedang down
  • Anda tidak dapat membuat direktori tempat file harus disimpan
  • Alat yang Anda perlukan untuk menjalankan skrip tidak ada
  • Anda tidak dapat mengumpulkan laporan karena mesin jarak jauh Anda mogok
  • Satu atau beberapa laporan rusak

Versi skrip saat ini bermasalah—Ini akan berjalan dari awal hingga akhir, error atau tidak:

./ collect_data_from_servers.sh Kunjungan:macMini2visiting:mac-pro-1-visiting:dmaf5lshw-dump.json 100% 54kb 48.8mb/s 00:00 scp:/var/log/lshw-dump.json :Tidak ada file atau direktori seperti ituscp:/var/log/lshw-dump.json:Tidak ada kesalahan file atau directoryparse tersebut:Pemisah yang diharapkan antara nilai pada baris 3, kolom 9

Selanjutnya, saya mendemonstrasikan beberapa hal untuk membuat skrip Anda lebih kuat dan terkadang pulih dari kegagalan.

Opsi nuklir:Gagal keras, gagal cepat

Cara yang tepat untuk menangani kesalahan adalah untuk memeriksa apakah program selesai dengan sukses atau tidak, menggunakan kode kembali. Kedengarannya jelas tetapi mengembalikan kode, nomor integer yang disimpan di bash $? atau $! variabel, terkadang memiliki arti yang lebih luas. Halaman manual bash memberi tahu Anda:

Untuk tujuan shell, perintah yang keluar dengan status
keluar nol telah berhasil. Status keluar nol menunjukkan keberhasilan.
Status keluar bukan nol menunjukkan kegagalan. Saat perintah
berakhir pada sinyal fatal N, bash menggunakan nilai 128+N sebagai
status keluar.

Seperti biasa, Anda harus selalu membaca halaman manual dari skrip yang Anda panggil, untuk melihat apa konvensi untuk masing-masing skrip. Jika Anda telah memprogram dengan bahasa seperti Java atau Python, kemungkinan besar Anda sudah familiar dengan pengecualian mereka, arti yang berbeda, dan bagaimana tidak semuanya ditangani dengan cara yang sama.

Jika Anda menambahkan set -o errexit ke skrip Anda, sejak saat itu akan membatalkan eksekusi jika ada perintah dengan kode !=0 . Tapi errexit tidak digunakan saat menjalankan fungsi di dalam if kondisi, jadi daripada mengingat pengecualian itu, saya lebih suka melakukan penanganan kesalahan eksplisit.

Lihat skrip versi kedua. Ini sedikit lebih baik:

1 #!/bin/bash2 # Skrip untuk mengumpulkan status keluaran lshw dari server rumah3 # Dependensi:4 # * LSHW:http://ezix.org/project/wiki/HardwareLiSter5 # * JQ:http://stedolan.github.io/jq/6 #7 # Di setiap mesin Anda dapat menjalankan sesuatu seperti ini dari cron (Tidak tahu CRON, jangan khawatir:https://crontab-generator.org/ ) 8 # 0 0 * * * /usr/sbin/lshw -json -quiet> /var/log/lshw-dump.json9 Penulis:Jose Vicente Nunez10 #11 set -o errtrace # Aktifkan jebakan err, kode akan dipanggil saat terjadi kesalahan terdeteksi12 jebakan "echo ERROR:Ada kesalahan dalam ${FUNCNAME-main context}, detail mengikuti" ERR13 mendeklarasikan -a server=(14 macmini215 mac-pro-1-116 dmaf517 )18 19 DATADIR="$HOME/ Documents/lshw-dump"20 jika [ ! -d "$DATADIR" ]; lalu 21   /usr/bin/mkdir -p -v "$DATADIR"|| "FATAL:Gagal membuat $DATADIR" &&exit 10022 fi 23 mendeklarasikan -A server_pid24 untuk server di ${servers[*]}; do25   echo "Mengunjungi:$server"26   /usr/bin/scp -o logLevel=Error ${server}:/var/log/lshw-dump.json ${DATADIR}/lshw-$server-dump.json &27   server_pid [$server]=$! # Simpan PID dari scp  dari server yang diberikan untuk nanti28 done29 # Iterate melalui semua server dan:30 # Tunggu kode kembalian masing-masing31 # Periksa kode keluar dari setiap scp32 untuk server di ${!server_pid[*]}; do33   tunggu ${server_pid[$server]}34   uji $? -ne 0 &&echo "ERROR:Salin dari $server bermasalah, tidak akan dilanjutkan" &&keluar 10035 done36 untuk lshw di $(/usr/bin/find $DATADIR -type f -name 'lshw-*-dump.json' ); do37   /usr/bin/jq '.["product","vendor", "configuration"]' $lshw38 selesai 

Inilah yang berubah:

  • Baris 11 dan 12, saya mengaktifkan pelacakan kesalahan dan menambahkan 'jebakan' untuk memberi tahu pengguna bahwa ada kesalahan dan ada turbulensi di depan. Anda mungkin ingin mematikan skrip Anda di sini, saya akan menunjukkan kepada Anda mengapa itu mungkin bukan yang terbaik.
  • Baris 20, jika direktori tidak ada, coba buat di baris 21. Jika pembuatan direktori gagal, keluar dengan kesalahan.
  • Pada baris 27, setelah menjalankan setiap pekerjaan latar belakang, saya menangkap PID dan mengaitkannya dengan mesin (hubungan (1:1).
  • Pada baris 33-35, saya menunggu scp tugas untuk diselesaikan, dapatkan kode pengembalian, dan jika itu kesalahan, batalkan.
  • Pada baris 37, saya memeriksa apakah file dapat diuraikan, jika tidak, saya keluar dengan kesalahan.

Jadi bagaimana penanganan kesalahan sekarang?

Kunjungan:MacMini2Visiting:mac-pro-1-visiting:dmaf5lshw-dump.json 100% 54kb 146.1mb/s 00:00 scp:/var/log/lshw-dump.json:tidak ada file tersebut atau directoryERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiERROR:Salin dari mac-pro-1-1 mengalami masalah, tidak akan berlanjutcp:/var/log/lshw-dump.json:Tidak ada file atau direktori seperti itu

Seperti yang Anda lihat, versi ini lebih baik dalam mendeteksi kesalahan tetapi sangat tak kenal ampun. Juga, itu tidak mendeteksi semua kesalahan, bukan?

Saat Anda buntu dan ingin memiliki alarm

Kode terlihat lebih baik, kecuali terkadang scp bisa macet di server (saat mencoba menyalin file) karena server terlalu sibuk untuk merespons atau hanya dalam kondisi buruk.

Contoh lain adalah mencoba mengakses direktori melalui NFS di mana $HOME dipasang dari server NFS:

/usr/bin/find $HOME -type f -name '*.csv' -print -fprint /tmp/report.txt 

Dan Anda menemukan beberapa jam kemudian bahwa titik pemasangan NFS sudah kedaluwarsa dan skrip Anda macet.

Batas waktu adalah solusinya. Dan, batas waktu GNU datang untuk menyelamatkan:

/usr/bin/timeout --kill-after 20.0s 10.0s /usr/bin/find $HOME -type f -name '*.csv' -print -fprint /tmp/report.txt 

Di sini Anda mencoba untuk secara teratur membunuh (sinyal TERM) proses dengan baik setelah 10,0 detik setelah dimulai. Jika masih berjalan setelah 20,0 detik, maka kirim sinyal KILL (kill -9 ). Jika ragu, periksa sinyal mana yang didukung di sistem Anda (kill -l , misalnya).

Jika ini tidak jelas dari dialog saya, lihat skrip untuk kejelasan lebih lanjut.

/usr/bin/time /usr/bin/timeout --kill-after=10.0s 20.0s /usr/bin/sleep 60sreal    0m20.003suser    0m0.000ssys    0m0.003s 

Kembali ke skrip asli untuk menambahkan beberapa opsi lagi dan Anda memiliki versi tiga:

 1 #!/bin/bash 2 # Script untuk mengumpulkan status keluaran lshw dari server rumah 3 # Dependensi:4 # * Buka SSH:http://www.openssh.com/portable.html 5 # * LSHW:http://ezix.org/project/wiki/HardwareLiSter 6 # * JQ:http://stedolan.github.io/jq/ 7 # * batas waktu:https://www.gnu.org/software /coreutils/ 8 #9 # Di setiap mesin Anda dapat menjalankan sesuatu seperti ini dari cron (Tidak tahu CRON, jangan khawatir:https://crontab-generator.org/) 10 # 0 0 * * * /usr/sbin /lshw -json -quiet> /var/log/lshw-dump.json 11 # Penulis:Jose Vicente Nunez 12 # 13 set -o errtrace # Aktifkan jebakan err, kode akan dipanggil ketika kesalahan terdeteksi 14 jebakan "echo KESALAHAN:Ada kesalahan dalam ${FUNCNAME-main context}, detail mengikuti" ERR 15 16 mendeklarasikan -a dependencies=(/usr/bin/timeout /usr/bin/ssh /usr/bin/jq) 17 untuk ketergantungan di ${dependensi[@]}; lakukan 18     jika [ ! -x $ketergantungan ]; kemudian 19         echo "ERROR:Missing $dependency" 20         exit 100 21     fi 22 selesai 23 24 mendeklarasikan -a server=( 25 macmini2 26 mac-pro-1-1 27 dmaf5 28 ) 29 30 function remote_copy { 31     server lokal=$1 32 echo "Visiting:$server" 33     /usr/bin/timeout --kill-after 25.0s 20.0s \ 34         /usr/bin/scp \ 35             -o BatchMode=yes \ 36             -o log \ Level=Error =5 \ 38             -o ConnectionAttempts=3 \ 39             ${server}:/var/log/lshw-dump.json ${DATADIR}/lshw-$server-dump.json 40     return $? 41 } 42 43 DATADIR="$HOME/Documents/lshw-dump" 44 jika [ ! -d "$DATADIR" ]; lalu 45     /usr/bin/mkdir -p -v "$DATADIR"|| "FATAL:Gagal membuat $DATADIR" &&exit 100 46 fi 47 mendeklarasikan -A server_pid 48 untuk server di ${servers[*]}; lakukan 49     remote_copy $server &50     server_pid[$server]=$! # Simpan PID scp  dari server yang diberikan untuk nanti 51 selesai 52 # Ulangi semua server dan:53 # Tunggu kode pengembalian masing-masing 54 # Periksa kode keluar dari setiap scp 55 untuk server di ${!server_pid [*]}; apakah 56     tunggu ${server_pid[$server]} 57     uji $? -ne 0 &&echo "ERROR:Salin dari $server bermasalah, tidak akan dilanjutkan" &&exit 100 58 done 59 for lshw di $(/usr/bin/find $DATADIR -type f -name 'lshw-*-dump. json'); lakukan 60     /usr/bin/jq '.["product","vendor", "configuration"]' $lshw 61 selesai 

Apa saja perubahannya?:

  • Antara baris 16-22, periksa apakah semua alat ketergantungan yang diperlukan ada. Jika tidak bisa dijalankan, maka 'Houston kita punya masalah.'
  • Membuat remote_copy fungsi, yang menggunakan batas waktu untuk memastikan scp selesai paling lambat 45.0 detik—baris 33.
  • Menambahkan batas waktu koneksi 5 detik, bukan default TCP—baris 37.
  • Menambahkan percobaan ulang ke scp pada baris 38—3 upaya yang masing-masing menunggu 1 detik.

Ada cara lain untuk mencoba lagi saat terjadi kesalahan.

Menunggu akhir dunia-bagaimana dan kapan harus mencoba lagi

Anda melihat ada pencobaan ulang yang ditambahkan ke scp memerintah. Tapi itu mencoba ulang hanya untuk koneksi yang gagal, bagaimana jika perintah gagal di tengah penyalinan?

Terkadang Anda ingin gagal begitu saja karena sangat kecil peluang untuk pulih dari suatu masalah. Sebuah sistem yang memerlukan perbaikan perangkat keras, misalnya, atau Anda dapat gagal kembali ke mode terdegradasi—artinya Anda dapat melanjutkan pekerjaan sistem Anda tanpa data yang diperbarui. Dalam kasus tersebut, tidak masuk akal untuk menunggu selamanya tetapi hanya untuk jangka waktu tertentu.

Berikut adalah perubahan pada remote_copy , agar singkat ini (versi empat):

#!/bin/bash# Kode dihilangkan untuk kejelasan...declare REMOTE_FILE="/var/log/lshw-dump.json"declare MAX_RETRIES=3# Blah blah blah...function remote_copy {    lokal server=$1    percobaan lokal=$2    lokal sekarang=1    status=0    while [ $now -le $retry ]; do        echo "INFO:Mencoba menyalin file dari:$server, effort=$now"        /usr/bin/timeout --kill-after 25.0s 20.0s \            /usr/bin/scp \                -o \    logLevel=Error \                -o ConnectTimeout=5 \                -o ConnectionAttempts=3 \                ${server}:$REMOTE_FILE ${DATADIR}/lshw-json status  ? jika [ $status -ne 0 ]; lalu            sleep_time=$(((RANDOM % 60)+ 1))            echo "PERINGATAN:Penyalinan gagal untuk $server:$REMOTE_FILE. Menunggu '${sleep_time} detik' sebelum mencoba kembali..."            /usr/bin/sleep ${sleep_time}s        else            istirahat # Baiklah, tidak ada gunanya menunggu...        fi        ((now=now+1))    selesai    return $status}DATADIR="$HOME/Documents/lshw-dump"if [ ! -d "$DATADIR" ]; lalu    /usr/bin/mkdir -p -v "$DATADIR"|| "FATAL:Gagal membuat $DATADIR" &&keluar 100fideclare -Server_pidfor server di ${servers[*]}; lakukan    remote_copy $server $MAX_RETRIES &    server_pid[$server]=$! # Simpan PID dari scp  dari server yang diberikan untuk nanti# Ulangi semua server dan:# Tunggu kode pengembalian masing-masing# Periksa kode keluar dari setiap server scpfor di ${!server_pid[*]}; apakah    tunggu ${server_pid[$server]}    menguji $? -ne 0 &&echo "ERROR:Salin dari $server bermasalah, tidak akan dilanjutkan" &&exit 100done# Blah bla bla, proses file yang baru saja Anda salin... 

Bagaimana tampilannya sekarang? Dalam proses ini, saya memiliki satu sistem mati (mac-pro-1-1) dan satu sistem tanpa file (macmini2). Anda dapat melihat bahwa salinan dari server dmaf5 langsung berfungsi, tetapi untuk dua lainnya, ada percobaan ulang untuk waktu acak antara 1 dan 60 detik sebelum keluar:

INFO:Mencoba menyalin file dari:macmini2, effort=1INFO:Mencoba menyalin file dari:mac-pro-1-1, effort=1INFO:Mencoba menyalin file dari:dmaf5, effort=1scp:/var/log/lshw-dump.json:Tidak ada file atau direktori tersebutERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiPERINGATAN:Penyalinan gagal untuk macmini2:/var/log/lshw-dump.json. Menunggu '60 detik' sebelum mencoba kembali...ssh:sambungkan ke host mac-pro-1-1 port 22:Tidak ada rute ke hostERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiPERINGATAN:Penyalinan gagal untuk mac-pro -1-1:/var/log/lshw-dump.json. Menunggu '32 detik' sebelum mencoba kembali...INFO:Mencoba menyalin file dari:mac-pro-1-1, upaya=2ssh:sambungkan ke host mac-pro-1-1 port 22:Tidak ada rute ke hostERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiPERINGATAN:Penyalinan gagal untuk mac-pro-1-1:/var/log/lshw-dump.json. Menunggu '18 detik' sebelum mencoba kembali...INFO:Mencoba menyalin file dari:macmini2, effort=2scp:/var/log/lshw-dump.json:No such file or directoryERROR:Ada kesalahan dalam konteks utama , detail untuk diikutiPERINGATAN:Penyalinan gagal untuk macmini2:/var/log/lshw-dump.json. Menunggu '3 detik' sebelum mencoba kembali...INFO:Mencoba menyalin file dari:macmini2, effort=3scp:/var/log/lshw-dump.json:No such file or directoryERROR:Ada kesalahan dalam konteks utama , detail untuk diikutiPERINGATAN:Penyalinan gagal untuk macmini2:/var/log/lshw-dump.json. Menunggu '6 detik' sebelum mencoba kembali...INFO:Mencoba menyalin file dari:mac-pro-1-1, upaya=3ssh:sambungkan ke host mac-pro-1-1 port 22:Tidak ada rute ke hostERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiPERINGATAN:Penyalinan gagal untuk mac-pro-1-1:/var/log/lshw-dump.json. Menunggu '47 detik' sebelum mencoba kembali...ERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiERROR:Salin dari mac-pro-1-1 bermasalah, tidak akan dilanjutkan 

Jika saya gagal, apakah saya harus mengulanginya lagi? Menggunakan pos pemeriksaan

Misalkan penyalinan jarak jauh adalah operasi paling mahal dari seluruh skrip ini dan Anda bersedia atau dapat menjalankan ulang skrip ini, mungkin menggunakan cron atau melakukannya dengan tangan dua kali dalam sehari untuk memastikan Anda mengambil file jika satu atau beberapa sistem tidak berfungsi.

Anda dapat, untuk hari itu, membuat 'cache status' kecil, di mana Anda hanya merekam operasi pemrosesan yang berhasil per mesin. Jika ada sistem di sana, jangan repot-repot memeriksa lagi untuk hari itu.

Beberapa program, seperti Ansible, melakukan hal serupa dan memungkinkan Anda untuk mencoba kembali buku pedoman pada sejumlah mesin terbatas setelah kegagalan (--limit @/home/user/site.retry ).

Versi baru (versi lima) skrip memiliki kode untuk merekam status salinan (baris 15-33):

15 mendeklarasikan SCRIPT_NAME=$(/usr/bin/basename $BASH_SOURCE)|| exit 10016 nyatakan YYYYMMDD=$(/usr/bin/date +%Y%m%d)|| exit 10017 mendeklarasikan CACHE_DIR="/tmp/$SCRIPT_NAME/$YYYYMMDD"18 # Logika untuk membersihkan direktori cache setiap hari tidak ditampilkan di sini19 jika [ ! -d "$CACHE_DIR" ]; lalu20  /usr/bin/mkdir -p -v "$CACHE_DIR"|| exit 10021 fi22 trap "/bin/rm -rf $CACHE_DIR" fungsi INT KILL2324 check_previous_run {25  mesin lokal=$126  test -f $CACHE_DIR/$machine &&return 0|| return 127 }2829 function mark_previous_run {30    machine=$131    /usr/bin/touch $CACHE_DIR/$machine32    return $?33 } 

Apakah Anda memperhatikan jebakan di jalur 22? Jika skrip terputus (mati), saya ingin memastikan seluruh cache tidak valid.

Dan kemudian, tambahkan logika pembantu baru ini ke dalam remote_copy fungsi (baris 52-81):

52 function remote_copy {53    server lokal=$154    check_previous_run $server55    test $? -eq 0 &&echo "INFO:$1 berhasil dijalankan sebelumnya. Tidak melakukan lagi" &&return 056    local retries=$257    local now=158    status=059    while [ $now -le $retries ]; do60        echo "INFO:Mencoba menyalin file dari:$server, effort=$now"61        /usr/bin/timeout --kill-after 25.0s 20.0s \62            /usr/bin/scp \63                -o Batch \64                -o logLevel=Error \65                -o ConnectTimeout=5 \66               -o ConnectionAttempts=3 \67              $  ${FILE_} $status -ne 0 ]; then70            sleep_time=$(((RANDOM % 60)+ 1))71            echo "PERINGATAN:Penyalinan gagal untuk $server:$REMOTE_FILE. Menunggu '${sleep_time} detik' sebelum mencoba kembali..."72            /usr/bin /sleep ${sleep_time}s73        else74            istirahat # Baiklah, tidak ada gunanya menunggu...75        fi76        ((now=now+1))77    done78    test $status -eq 0 &&mark_previous_run $? -ne 0 &&status=180    kembalikan $status81 } 

Saat pertama kali dijalankan, pesan baru untuk direktori cache akan dicetak:

./collect_data_from_servers.v5.sh/usr/bin/mkdir:direktori yang dibuat '/tmp/collect_data_from_servers.v5.sh'/usr/bin/mkdir:direktori yang dibuat '/tmp/collect_data_from_servers.v5.sh //20210612'ERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiINFO:Mencoba menyalin file dari:macmini2, upaya=1ERROR:Ada kesalahan dalam konteks utama, detail untuk diikuti 

Jika Anda menjalankannya lagi, skrip mengetahui bahwa dma5f baik untuk pergi, tidak perlu mencoba lagi salinan:

./collect_data_from_servers.v5.shINFO:dmaf5 berhasil dijalankan sebelumnya. Tidak melakukan lagiERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiINFO:Mencoba menyalin file dari:macmini2, upaya=1ERROR:Ada kesalahan dalam konteks utama, detail untuk diikutiINFO:Mencoba menyalin file dari:mac-pro- 1-1, upaya=1 

Bayangkan bagaimana kecepatannya saat Anda memiliki lebih banyak mesin yang tidak boleh dikunjungi kembali.

Meninggalkan remah-remah di belakang:Apa yang harus dicatat, cara mencatat, dan keluaran verbose

Jika Anda seperti saya, saya suka sedikit konteks untuk dikorelasikan ketika terjadi kesalahan. gema pernyataan pada skrip bagus tetapi bagaimana jika Anda dapat menambahkan stempel waktu ke dalamnya.

Jika Anda menggunakan logger , Anda dapat menyimpan hasilnya di journalctl untuk ditinjau nanti (bahkan agregasi dengan alat lain di luar sana). Bagian terbaiknya adalah Anda menunjukkan kekuatan journalctl segera.

Jadi, alih-alih hanya melakukan echo , Anda juga dapat menambahkan panggilan ke logger seperti ini menggunakan fungsi bash baru yang disebut ‘message ':

SCRIPT_NAME=$(/usr/bin/basename $BASH_SOURCE)|| keluar 100FULL_PATH=$(/usr/bin/realpath ${BASH_SOURCE[0]})|| exit 100set -o errtrace # Aktifkan jebakan err, kode akan dipanggil ketika kesalahan terdeteksitrap "echo ERROR:Ada kesalahan di ${FUNCNAME[0]-main context}, detail menyusul" ERRdeclare CACHE_DIR="/tmp /$SCRIPT_NAME/$YYYYMMDD"function message {    message="$1"    func_name="${2-unknown}"    priority=6    if [ -z "$2" ]; lalu        echo "INFO:" $message    else        echo "ERROR:" $message        priority=0    fi    /usr/bin/logger --journald< 

Anda dapat melihat bahwa Anda dapat menyimpan kolom terpisah sebagai bagian dari pesan, seperti prioritas, skrip yang menghasilkan pesan, dll.

Jadi bagaimana ini berguna? Nah, Anda bisa mendapatkan pesan antara 13:26 dan 13:27, hanya kesalahan (priority=0 ) dan hanya untuk skrip kami (collect_data_from_servers.v6.sh ) seperti ini, output dalam format JSON:

journalctl --sejak 13:26 --sampai 13:27 --output json-pretty PRIORITY=0 MESSAGE_ID=collect_data_from_servers.v6.sh 
{        "_BOOT_ID" :"dfcda9a1a1cd406ebd88a339bec96fb6",        "_AUDIT_LOGINUID" :"1000",        "SYSLOG_IDENTIFIER" :"TRANS", 0   SEL :"unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",        "__REALTIME_TIMESTAMP" :"1623518797641880",        "_AUDIT_SESSION" :"3",      ESS_ "_GID" sh",        "MESSAGE" :"Pesan gagal untuk macmini2:/var/log/lshw-dump.json. Menunggu '45 detik' sebelum mencoba kembali...",        "_CAP_EFFECTIVE" :"0",        "CODE_FUNC" :"remote_copy",        "_MACHINE_ID" :"60d7a3f69b674aaebb600c0e82e01d05",        "_COMM" :"logger",        "CODE_FILE" :"/home/josevnz/BashError_/collect_data"_P32sh  . :"25928272252",        "_HOSTNAME" :"dmaf5",        "_SOURCE_REALTIME_TIMESTAMP" :"1623518797641843",        "__CURSOR" :"s=97bb6295795a4560ad6f dedd8143df97;i=1f826;b=dfcda9a1a1cd406ebd88a339bec96fb6;m=60972097c;t=5c494ed383898;x=921c71966b8943e3",        "_UID" :"1000"} 

Karena ini adalah data terstruktur, pengumpul log lain dapat memeriksa semua mesin Anda, menggabungkan log skrip, dan kemudian Anda tidak hanya memiliki data tetapi juga informasi.

Anda dapat melihat seluruh versi enam skrip.

Jangan terlalu bersemangat untuk mengganti data Anda sampai Anda memeriksanya.


Jika Anda perhatikan sejak awal, saya telah menyalin file JSON yang rusak berulang kali:

Kesalahan penguraian:Pemisah yang diharapkan antara nilai pada baris 4, penguraian kolom 11ERROR '/home/josevnz/Documents/lshw-dump/lshw-dmaf5-dump.json' 

Itu mudah untuk dicegah. Salin file ke lokasi sementara dan jika file rusak, jangan coba mengganti versi sebelumnya (dan biarkan yang buruk untuk diperiksa. baris 99-107 dari versi tujuh skrip):

function remote_copy {    server lokal=$1    check_previous_run $server    test $? -eq 0 &&pesan "$1 berhasil dijalankan sebelumnya. Tidak melakukan lagi" &&return 0    local retries=$2    local now=1    status=0    while [ $now -le $retries ]; do        pesan "Mencoba menyalin file dari:$server, effort=$now"        /usr/bin/timeout --kill-after 25.0s 20.0s \            /usr/bin/scp \                -o \   =yes Kesalahan \                -o ConnectTimeout=5 \                -o ConnectionAttempts=3 \                ${server}:$REMOTE_FILE ${DATADIR}/lshw-$server-dump status  $. jika [ $status -ne 0 ]; lalu            sleep_time=$(((RANDOM % 60)+ 1))            pesan "Penyalinan gagal untuk $server:$REMOTE_FILE. Menunggu '${sleep_time} detik' sebelum mencoba kembali..." ${FUNCNAME[0]}            / usr/bin/sleep ${sleep_time}s        else            istirahat # Baiklah, tidak ada gunanya menunggu...        fi        ((now=now+1))    selesai    if [ $status -eq 0 ]; lalu        /usr/bin/jq '.' ${DATADIR}/lshw-$server-dump.json.$$> /dev/null 2>&1        status=$? jika [ $status -eq 0 ]; lalu            /usr/bin/mv -v -f ${DATADIR}/lshw-$server-dump.json.$$ ${DATADIR}/lshw-$server-dump.json &&mark_previous_run $server            test $? -ne 0 &&status=1        else            pesan "${DATADIR}/lshw-$server-dump.json.$$ Rusak. Meninggalkan untuk diperiksa..." ${FUNCNAME[0]}        fi    fi    return $status} 

Pilih alat yang tepat untuk tugas tersebut dan siapkan kode Anda dari baris pertama

Salah satu aspek yang sangat penting dari penanganan kesalahan adalah pengkodean yang tepat. Jika Anda memiliki logika yang buruk dalam kode Anda, tidak ada penanganan kesalahan yang akan membuatnya lebih baik. Agar ini tetap singkat dan terkait dengan bash, saya akan memberi Anda beberapa petunjuk di bawah ini.

Anda harus SELALU memeriksa sintaks kesalahan sebelum menjalankan skrip Anda:

bash -n $my_bash_script.sh 

Dengan serius. Itu harus otomatis seperti melakukan tes lainnya.

Baca halaman manual bash dan kenali opsi yang harus diketahui, seperti:

set -xvmy_complicated_instruction1my_complicated_instruction2my_complicated_instruction3set +xv 

Gunakan ShellCheck untuk memeriksa skrip bash Anda

Sangat mudah untuk melewatkan masalah sederhana ketika skrip Anda mulai tumbuh besar. ShellCheck adalah salah satu alat yang menyelamatkan Anda dari membuat kesalahan.

shellcheck collect_data_from_servers.v7.shIn collect_data_from_servers.v7.sh baris 15:untuk ketergantungan pada ${dependencies[@]}; lakukan                  ^----------------^ SC2068:Ekspansi larik tanda kutip ganda untuk menghindari elemen pemisahan ulang.Dalam baris collect_data_from_servers.v7.sh 16:    if [ ! -x $ketergantungan ]; lalu              ^---------^ SC2086:Tanda kutip ganda untuk mencegah penggelembungan dan pemisahan kata. Apakah maksud Anda:     if [ ! -x "$ketergantungan" ]; lalu... 

Jika Anda bertanya-tanya, versi final skrip, setelah melewati ShellCheck ada di sini. Sangat bersih.

Anda melihat sesuatu dengan proses scp latar belakang

Anda mungkin memperhatikan bahwa jika Anda mematikan skrip, itu meninggalkan beberapa proses bercabang di belakang. Itu tidak baik dan inilah salah satu alasan saya lebih suka menggunakan alat seperti Ansible atau Parallel untuk menangani jenis tugas ini di beberapa host, membiarkan kerangka kerja melakukan pembersihan yang tepat untuk saya. Anda tentu saja dapat menambahkan lebih banyak kode untuk menangani situasi ini.

Skrip bash ini berpotensi membuat bom garpu. Itu tidak memiliki kendali atas berapa banyak proses yang muncul pada saat yang sama, yang merupakan masalah besar dalam lingkungan produksi nyata. Juga, ada batasan berapa banyak sesi ssh bersamaan yang dapat Anda miliki (apalagi mengkonsumsi bandwidth). Sekali lagi, saya menulis contoh fiktif ini di bash untuk menunjukkan kepada Anda bagaimana Anda selalu dapat meningkatkan program untuk menangani kesalahan dengan lebih baik.

Mari kita rekap

[ Unduh sekarang:Panduan sysadmin untuk skrip Bash. ]

1.  Anda harus memeriksa kode pengembalian dari perintah Anda. Itu bisa berarti memutuskan untuk mencoba lagi sampai kondisi sementara membaik atau membuat hubungan pendek seluruh skrip.
2. Berbicara tentang kondisi sementara, Anda tidak perlu memulai dari awal. Anda dapat menyimpan status tugas yang berhasil, lalu mencoba lagi sejak saat itu dan seterusnya.
3. Bash 'perangkap' adalah teman Anda. Gunakan untuk pembersihan dan penanganan kesalahan.
4. Saat mengunduh data dari sumber apa pun, anggap itu rusak. Jangan pernah menimpa kumpulan data yang baik dengan data baru sampai Anda melakukan beberapa pemeriksaan integritas.
5. Manfaatkan journalctl dan bidang khusus. Anda dapat melakukan penelusuran canggih untuk mencari masalah, dan bahkan mengirimkan data tersebut ke agregator log.
6. Anda dapat memeriksa status tugas latar belakang (termasuk sub-kulit). Ingatlah untuk menyimpan PID dan menunggunya.
7. Dan terakhir:Gunakan pembantu lint Bash seperti  ShellCheck. Anda dapat menginstalnya di editor favorit Anda (seperti VIM atau PyCharm). Anda akan terkejut betapa banyak kesalahan yang tidak terdeteksi pada skrip Bash...

Jika Anda menikmati konten ini atau ingin mengembangkannya, hubungi tim di [email protected].


Linux
  1. Penanganan kesalahan dalam skrip Bash

  2. Typeset -a Apakah Memberikan Kesalahan Dalam Script?

  3. Variabel Curl Outfile Tidak Bekerja Dalam Skrip Bash?

  1. Kesalahan Skrip Bash:Ekspresi Integer Diharapkan?

  2. Bash:Kesalahan Sintaks Dekat Token Tak Terduga `}'?

  3. Skrip Bash - Contoh Halo Dunia

  1. Bash mengabaikan kesalahan untuk perintah tertentu

  2. Cara mendeklarasikan array 2D di bash

  3. Naikkan kesalahan dalam skrip Bash