Apakah ada cara untuk memiliki fungsi dalam skrip bash saya dijalankan secara otomatis pada setiap kesalahan perintah?

12

Saya sedang menulis skrip shell yang perlu melakukan banyak perintah dan setiap perintah tergantung pada setiap perintah sebelumnya. Jika ada perintah yang gagal, seluruh skrip harus gagal dan saya memanggil fungsi keluar. Saya dapat memeriksa kode keluar dari setiap perintah tetapi saya bertanya-tanya apakah ada mode yang dapat saya aktifkan atau cara untuk mendapatkan bash untuk melakukan itu secara otomatis.

Misalnya, ambil perintah ini:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


Apakah ada cara di mana saya entah bagaimana dapat memberi sinyal ke shell sebelum menjalankan perintah-perintah ini yang seharusnya memanggil fungsi saya jika salah satu dari mereka gagal, sehingga saya dapat menulis sesuatu yang lebih bersih seperti:

cd foo
rm a
cd bar
rm b
uji
sumber

Jawaban:

13

Anda dapat menggunakan bash trap ERR untuk membuat skrip Anda keluar jika ada perintah yang mengembalikan status lebih besar dari nol dan menjalankan fungsi Anda saat keluar.

Sesuatu seperti:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Perhatikan bahwa bash trap ERR tidak tersirat set -o errexitatau set -edan bukan POSIX.

Dan ERRjebakan tidak dieksekusi jika perintah yang gagal adalah bagian dari daftar perintah segera mengikuti untilatau whilekata kunci, bagian dari tes mengikuti kata-kata ifatau elifdicadangkan, bagian dari perintah yang dieksekusi dalam &&atau ||daftar, atau jika status pengembalian perintah sedang terbalik menggunakan !.

cuonglm
sumber
1

Variasi (mungkin) yang lebih sederhana pada jawaban yang diterima:

  1. Gunakan set -e untuk menyebabkan kegagalan satu perintah untuk membatalkan eksekusi daftar.
  2. Cukup daftarkan perintah Anda.
  3. Gunakan pernyataan if- then- elseuntuk menjalankan perintah penanganan kesalahan Anda. Bagian terakhir ini agak sulit. Menonton:
set -e
jika
    cmd 1                         # eg, cd foo
     cmd 2                         # eg, rm a
     cmd 3                         # eg, cd bar
     cmd 4                         # eg, rm b
kemudian
    set + e
    perintah untuk dilakukan setelah sukses (jika ada)
lain
    set + e
    fungsi saya
    perintah lain yang harus dilakukan setelah kegagalan (jika ada) 
fi

Bagian yang sulit adalah bahwa Anda menempatkan perintah Anda ke dalam ifbagian dari if- then- else, bukan thensebagian atau elsebagian. Ingat bahwa sintaks dari ifpernyataan tersebut adalah

jika  daftar ; kemudian  daftar ; [  daftar elif ; kemudian  daftar ; ] ... [  daftar lain ; ] fi 
   ↑↑↑↑
The set -ememberitahu shell yang, jika ( ) gagal, seharusnya tidak terus dan mengeksekusi ( ), dan seterusnya ke bawah garis. Jika ini terjadi pada perintah di tingkat terluar dari skrip shell, shell akan keluar. Namun, karena · · · adalah daftar (gabungan) yang mengikuti suatu , kegagalan salah satu dari keempat perintah tersebut hanya menyebabkan seluruh daftar gagal - yang menyebabkan klausa dieksekusi. Jika keempat perintah berhasil, klausa dieksekusi.cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

Dalam kedua kasus tersebut, hal pertama yang harus Anda lakukan mungkin adalah menonaktifkan (mematikan) eopsi dengan melakukan set +e. Jika tidak, skrip mungkin akan terhempas keluar dari air jika perintah myfuncgagal.

set -eadalah ditentukan dan dijelaskan dalam spesifikasi POSIX .

G-Man Mengatakan 'Reinstate Monica'
sumber
0

Diambil kata Anda " setiap perintah tergantung pada setiap perintah sebelumnya. Jika ada perintah gagal seluruh skrip harus gagal " secara harfiah, saya pikir Anda tidak memerlukan fungsi khusus untuk menangani kesalahan.

Yang Anda butuhkan adalah untuk menghubungkan perintah Anda dengan &&operator dan ||operator, yang melakukan apa yang Anda tulis.

Misalnya rantai ini akan rusak dan akan mencetak "ada yang salah" jika ada perintah sebelumnya yang rusak (bash dibaca dari kiri ke kanan)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Contoh nyata (saya membuat dir foo, file a, bar dir dan file b hanya untuk demo nyata):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

Dan akhirnya jika semua perintah telah berhasil dijalankan (kode keluar 0), skrip terus berjalan:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

Yang penting untuk diingat adalah bahwa dengan menggunakan perintah && selanjutnya dieksekusi jika perintah sebelumnya keluar dengan kode 0 yang untuk bash berarti sukses.

Jika ada perintah yang salah dalam rantai maka perintah / skrip / apa pun yang berikut || akan dieksekusi.

Dan hanya untuk catatan, Jika Anda perlu melakukan tindakan yang berbeda tergantung pada perintah yang rusak, Anda juga bisa melakukannya dengan skrip klasik dengan memantau nilai $?yang melaporkan kode keluar dari perintah sebelumnya persis (mengembalikan nol jika perintah dieksekusi berhasil atau angka positif lainnya jika perintah gagal)

Contoh:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Keluaran:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Kiat: Anda dapat menekan pesan "bash: cd: bbar: Tidak ada file seperti itu ..." dengan menerapkan eval $comm 2>/dev/null

George Vasiliou
sumber