keluar dikonfirmasi menggunakan perangkap

9

Saya mencoba menjebak Ctrl+Csinyal yang meminta konfirmasi dari pengguna. Bagian perangkap berfungsi dengan baik. Tapi begitu sinyal terjebak, itu tidak kembali ke eksekusi normal. Alih-alih, itu berhenti skrip. Cara membuatnya melanjutkan eksekusi ketika pengguna menekan no.

ini kodeku

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /
CHID
sumber

Jawaban:

12

Apa yang terjadi

Saat Anda menekan Ctrl+ C, SIGINTsinyal dikirim ke seluruh grup proses latar depan . Ini dikirim ke findproses dan proses shell panggilan. findbereaksi dengan keluar segera, dan shell bereaksi dengan memanggil perangkap.

Jika kode dalam jebakan kembali (yaitu tidak menelepon exit), eksekusi dilanjutkan dengan perintah setelah kode yang terputus oleh sinyal. Di sini, setelah findperintah muncul akhir skrip, jadi skrip segera keluar. Tetapi Anda dapat melihat perbedaan antara memasukkan 0 dan 1 dengan menambahkan perintah lain:

find /
echo "find returned $?"

Cara untuk melakukan apa yang Anda inginkan (tapi mungkin tidak boleh dilakukan)

Anda dapat melakukan apa yang Anda inginkan; tapi ini bagian dari jawaban saya lebih tentang menemukan pemrograman shell daripada menyelesaikan masalah yang sebenarnya.

  • Sebagai masalah desain, sinyal yang dapat dimulai kembali bukanlah yang biasanya Anda harapkan dalam jenis program yang relatif sederhana seperti shell script biasanya. Harapannya adalah Ctrl+ Cakan membunuh skrip.
  • Seperti yang akan Anda lihat di bawah, itu memperluas kemampuan shell agak jauh.

Jika Anda ingin menghindari pembunuhan find, Anda harus memulainya di latar belakang : find / &. Kemudian gunakan waitbuiltin untuk menunggu keluar secara normal. Sebuah sinyal akan mengganggu waitbuilt-in, yang dapat Anda jalankan dalam satu lingkaran sampai Anda menerima sinyal yang ingin Anda sebarkan. Kemudian gunakan killuntuk mematikan pekerjaan.

hell () {
  echo "Do you want to quit? Press 1 for yes and 0 for no"
  read n
  if [ "$n" = 1 ]; then
    # Kill the job if it's running, then exit
    if [ -n "$job_pid" ]; then kill $job_pid; fi
    exit 1
  fi
}

job_pid=
trap "hell" SIGINT

# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
  echo "resuming wait"
done
job_pid=
echo last exit code: $?

Ada batasan untuk pendekatan ini di shell:

  • Ada kondisi balapan: jika Anda menekan Ctrl+ Ctepat setelah pekerjaan selesai tetapi sebelum job_pid=garis, pawang sinyal akan mencoba untuk membunuh $jobpid, tetapi proses tidak lagi ada (bahkan sebagai zombie, karena waitsudah menuai), dan prosesnya ID mungkin telah digunakan kembali oleh proses lain. Ini tidak mudah diperbaiki di shell (mungkin dengan mengatur handler untuk SIGCHLD?).
  • Jika Anda memerlukan status pengembalian dari pekerjaan, Anda harus menggunakan wait $job_pidformulir. Tapi kemudian Anda tidak dapat membedakan " waitterganggu oleh sinyal" dari "pekerjaan terbunuh oleh sinyal" (atau dari "pekerjaan yang diakhiri dengan sendirinya dengan status pengembalian ≥128", tapi itu fakta umum dalam shell pemrograman).
  • Ini tidak akan meluas dengan mudah jika sama sekali ke beberapa orang. Perhatikan bahwa perilaku jebakan dan sinyal sering mengejutkan ketika Anda melampaui dasar-dasar di sebagian besar implementasi shell (hanya ksh yang melakukan ini dengan baik).

Untuk mengatasi keterbatasan ini, gunakan bahasa yang lebih bagus seperti Perl atau Python.

Gilles 'SANGAT berhenti menjadi jahat'
sumber