ยืนยันการออกโดยใช้กับดัก


9

ฉันพยายามดักCtrl+Cสัญญาณเพื่อขอคำยืนยันจากผู้ใช้ ส่วนการดักจับทำงานได้ดี แต่เมื่อสัญญาณถูกดักจับมันจะไม่กลับสู่การทำงานปกติ แต่จะออกจากสคริปต์แทน วิธีทำให้มันทำงานต่อเมื่อผู้ใช้กดหมายเลข

นี่คือรหัสของฉัน

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 /

คำตอบ:


12

เกิดอะไรขึ้น

เมื่อคุณกดCtrl+ Cที่SIGINTสัญญาณจะถูกส่งมอบให้กับทั้งกลุ่มกระบวนการเบื้องหน้า ที่นี่มันถูกส่งไปยังทั้งfindกระบวนการและกระบวนการเชลล์การเรียก findตอบสนองโดยออกจากทันทีและเชลล์ตอบสนองโดยการเรียกกับดัก

หากโค้ดในกับดักส่งคืน (เช่นไม่เรียกexit) การดำเนินการจะดำเนินการกับคำสั่งหลังจากที่ถูกขัดจังหวะโดยสัญญาณ ที่นี่หลังจากfindคำสั่งมาถึงจุดสิ้นสุดของสคริปต์ดังนั้นสคริปต์จะออกทันที แต่คุณสามารถเห็นความแตกต่างระหว่างการป้อน 0 และ 1 โดยการเพิ่มคำสั่งอื่น:

find /
echo "find returned $?"

วิธีทำสิ่งที่คุณต้องการ (แต่อาจไม่ควรทำ)

คุณสามารถทำสิ่งที่คุณต้องการ; แต่คำตอบของฉันส่วนนี้เกี่ยวกับการค้นพบการเขียนโปรแกรมเชลล์มากกว่าการแก้ปัญหาจริง

  • ในเรื่องของการออกแบบสัญญาณที่สามารถรีสตาร์ทได้นั้นไม่ใช่สิ่งที่คุณคาดหวังในรูปแบบของโปรแกรมที่ค่อนข้างง่ายที่เชลล์สคริปต์ปกติ ความคาดหวังคือCtrl+ Cจะฆ่าสคริปต์
  • ดังที่คุณเห็นด้านล่างมันยืดขีดความสามารถของเปลือกออกไปเล็กน้อย

หากคุณต้องการที่จะหลีกเลี่ยงการฆ่าfindคุณจะต้องเริ่มต้นในพื้นหลังfind / & : จากนั้นใช้waitbuiltinเพื่อรอให้มันออกตามปกติ สัญญาณจะขัดจังหวะการwaitติดตั้งในตัวซึ่งคุณสามารถทำงานเป็นแบบวนซ้ำจนกว่าคุณจะได้รับสัญญาณที่คุณต้องการเผยแพร่ จากนั้นใช้killเพื่อฆ่างาน

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: $?

มีข้อ จำกัด สำหรับวิธีการนี้ในเชลล์:

  • มีสภาพการแข่งขัน: ถ้าคุณกดCtrl+ Cหลังจากงานเสร็จสิ้น แต่ก่อนถึงjob_pid=บรรทัดผู้จัดการสัญญาณจะพยายามฆ่า$jobpidแต่กระบวนการไม่มีอยู่อีกต่อไป (แม้จะเป็นซอมบี้เพราะwaitได้ทำการเก็บเกี่ยวแล้ว) และกระบวนการ ID อาจถูกนำมาใช้ซ้ำโดยกระบวนการอื่น สิ่งนี้ไม่สามารถแก้ไขได้อย่างง่ายดายในเชลล์ (อาจโดยการตั้งค่าตัวจัดการSIGCHLDใช่ไหม)
  • หากคุณต้องการสถานะการส่งคืนจากงานคุณต้องใช้wait $job_pidแบบฟอร์ม แต่คุณไม่สามารถแยกความแตกต่าง“ waitถูกขัดจังหวะด้วยสัญญาณ” จาก“ งานถูกฆ่าโดยสัญญาณ” (หรือจาก“ งานถูกยกเลิกด้วยความสอดคล้องกับสถานะการส่งคืน≥128” แต่นั่นเป็นข้อเท็จจริงทั่วไปในเชลล์ การเขียนโปรแกรม)
  • สิ่งนี้จะไม่ขยายออกไปได้อย่างง่ายดายหากเลยถึงหลาย ๆ subjobs โปรดทราบว่าพฤติกรรมของกับดักและสัญญาณมักจะน่าแปลกใจเมื่อคุณก้าวไปไกลกว่าพื้นฐานในการใช้งานเชลล์ส่วนใหญ่ (เฉพาะ ksh เท่านั้นที่ทำได้ดี)

หากต้องการเอาชนะข้อ จำกัด เหล่านี้ให้ใช้ภาษาที่ชอบมากกว่าเช่น Perl หรือ Python

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.