“Trap… INT TERM EXIT” benar-benar diperlukan?


63

Banyak contoh untuk trapdigunakan trap ... INT TERM EXITuntuk tugas pembersihan. Tetapi apakah benar-benar perlu untuk mendaftar ketiga sigspec?

Manual mengatakan:

Jika SIGNAL_SPEC EXIT (0) ARG dijalankan saat keluar dari shell.

yang saya percaya berlaku apakah skrip selesai secara normal atau selesai karena diterima SIGINTatau SIGTERM. Eksperimen juga menegaskan keyakinan saya:

$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+  Interrupt               ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+  Terminated              ./trap-exit

Lalu mengapa begitu banyak contoh mendaftar semuanya INT TERM EXIT? Atau apakah saya melewatkan sesuatu dan apakah ada kasus di mana solonya EXITakan terlewatkan?


3
Juga perlu diingat bahwa dengan spek seperti INT TERM EXITkode pembersihan dijalankan dua kali ketika SIGTERMatau SIGINTditerima.
maxschlepzig

Jawaban:


19

The POSIX spesifikasi tidak mengatakan banyak tentang kondisi dihasilkan dalam melaksanakan perangkap EXIT, hanya tentang apa lingkungannya harus terlihat seperti ketika dieksekusi.

Dalam shell ash Busybox, tes keluar perangkap Anda tidak menggemakan 'TRAP' sebelum keluar karena SIGINT atau SIGTERM. Saya menduga ada beberapa kerang lain yang mungkin tidak berfungsi dengan baik.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

3
dashjuga tidak menjebak hanya EXITketika diterima SIGINT/SIGTERM.
maxschlepzig

4
zshjuga - dengan demikian, mungkin bashadalah satu-satunya shell di mana EXITjuga cocok dengan sinyal.
maxschlepzig

@maxschlepzig zshtidak menjebak EXITketika itu diterima INT, tetapi itu ketika menerima TERM. EDIT: Saya baru tahu berapa umur ini ...
JoL

27

Ya, ada perbedaan.

Skrip ini akan keluar saat Anda menekan Enter, atau mengirimnya SIGINTatau SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

Script ini akan keluar ketika Anda menekan Enter:

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Diuji dalam sh , Bash , dan Zsh . (tidak lagi berfungsi di sh saat Anda menambahkan perintah agar trap dijalankan)


Ada juga yang dikatakan @Shawn: Ash dan Dash tidak menangkap sinyal EXIT.

Jadi, untuk menangani sinyal dengan kuat, yang terbaik adalah menghindari perangkap EXITsama sekali, dan gunakan sesuatu seperti ini:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

1
Solusi dengan pembersihan melakukan hal yang benar - sangat elegan! Itu telah menjadi ungkapan untuk skrip bash saya dengan mktemppanggilan.
Bjoern Dahlgren

2
Apakah itu exitperlu cleanup?
jarno

3
Ini tidak berfungsi jika Anda memiliki kesalahan shellscript dalam kode Anda yang menyebabkannya keluar sebelum waktunya.
ijw

2
@ijw: Di Bash dan Ksh, Anda dapat menjebak ERRuntuk mengatasinya, tetapi tidak portabel .
Zaz

5
Solusi ini tidak kuat ketika shell lain menyebutnya. Itu tidak menangani menunggu di pintu keluar koperasi ; Anda akan ingin trap - INT TERM; kill -2 $$sebagai baris terakhir dari pembersihan, untuk memberi tahu induk shell bahwa ia keluar sebelum waktunya. Jika shell induk foobar.sh memanggil skrip Anda (foo.sh), dan kemudian memanggil bar.sh, Anda tidak ingin bar.sh dijalankan jika INT / TERM dikirimkan ke foo.sh. trap cleanup EXITakan menangani propagasi ini secara otomatis, jadi IMO yang paling kuat. Ini juga berarti Anda tidak perlu menelepon cleanupdi akhir skrip.
Nicholas Pipitone

12

Menyempurnakan jawaban terakhir, karena memiliki masalah:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Poin di atas:

Penangan INT dan TERM tidak berhenti untuk saya ketika saya menguji - mereka menangani kesalahan kemudian shell kembali ke keluar (dan ini tidak terlalu mengejutkan). Jadi saya memastikan bahwa pembersihan keluar setelah itu, dan dalam kasus sinyal selalu menggunakan kode kesalahan (dan dalam kasus keluar normal, mempertahankan kode kesalahan).

Dengan bash, sepertinya keluar di INT handler juga memanggil EXIT handler, maka saya membuka bukaan keluar handler dan menyebutnya sendiri (yang akan bekerja di shell apa pun tanpa peduli perilaku).

Saya perangkap keluar karena skrip shell dapat keluar sebelum mereka mencapai bagian bawah - kesalahan sintaks, set -e dan bukan nol kembali, cukup memanggil keluar. Anda tidak bisa mengandalkan shellscript untuk sampai ke bawah.

SIGQUIT adalah Ctrl- \ jika Anda belum pernah mencobanya. Memberi Anda bonus coredump. Jadi saya pikir ini juga layak untuk diperangkap, meskipun sedikit tidak jelas.

Pengalaman masa lalu mengatakan jika Anda (seperti saya) selalu menekan Ctrl-C beberapa kali, Anda kadang-kadang akan menangkapnya setengah jalan melalui bagian pembersihan skrip shell Anda, jadi ini berfungsi tetapi tidak selalu sesempurna yang Anda inginkan.


2
Penelepon hanya akan mendapatkan 1 sebagai kode keluar, tidak peduli sinyal apa yang menyebabkan pintu keluar, sementara withour trappemanggil akan mendapatkan 130 untuk SIGINT, 143 untuk SIGTERM, dll Jadi saya akan menangkap dan lulus kode keluar yang benar sebagai: sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }.
musiphil

2
Bisakah Anda memperjelas tujuan trap '' EXIT INT TERMfungsi pembersihan? Apakah ini untuk mencegah gangguan pengguna pembersihan yang Anda sebutkan di paragraf terakhir? Bukankah itu EXITberlebihan?
Enam
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.