เก็บรหัสทางออกเมื่อวางกับ SIGINT และคล้ายกันหรือไม่


14

หากฉันใช้trapเช่นอธิบายไว้ในhttp://linuxcommand.org/wss0160.php#trapเพื่อจับ ctrl-c (หรือคล้ายกัน) และการล้างข้อมูลก่อนออกจากนั้นฉันจะเปลี่ยนรหัสออกที่ส่งคืน

ตอนนี้อาจจะไม่สร้างความแตกต่างในโลกแห่งความเป็นจริง (เช่นเพราะรหัสออกไม่ได้พกพาและด้านบนของที่ไม่โปร่งใสตลอดเวลาตามที่กล่าวไว้ในรหัสทางออกเริ่มต้นเมื่อกระบวนการสิ้นสุดลง? ) แต่ฉันสงสัยว่ามี จริงๆแล้วไม่มีวิธีในการป้องกันและส่งคืนรหัสข้อผิดพลาดเริ่มต้นสำหรับสคริปต์ที่ถูกขัดจังหวะแทนหรือไม่

ตัวอย่าง (เป็น bash แต่คำถามของฉันไม่ควรพิจารณาเฉพาะ bash):

#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _

เอาท์พุท:

$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT 
EXIT
$ echo $?
1

(แก้ไขเพื่อลบเพื่อให้สอดคล้องกับ POSIX มากขึ้น)

(แก้ไขอีกครั้งเพื่อให้เป็นสคริปต์ทุบตีแทนคำถามของฉันไม่ได้เฉพาะเชลล์)

แก้ไขเพื่อใช้พกพา "INT" สำหรับกับดักเพื่อประโยชน์ของ "SIGINT" ที่ไม่พกพา

แก้ไขเพื่อลบเครื่องหมายปีกกาที่ไร้ประโยชน์และเพิ่มวิธีแก้ปัญหาที่อาจเกิดขึ้น

ปรับปรุง:

ฉันแก้ไขมันตอนนี้โดยเพียงแค่ออกด้วยรหัสข้อผิดพลาดบางอย่างที่ฮาร์ดโค้ดและดักจับ EXIT นี่อาจเป็นปัญหาในบางระบบเพราะรหัสข้อผิดพลาดอาจแตกต่างกันหรือกับดัก EXIT เป็นไปไม่ได้ แต่ในกรณีของฉันมันก็โอเคพอแล้ว

trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM

สคริปต์ของคุณดูแปลก: คุณบอกreadจะอ่านจาก coprocess ปัจจุบันและจะไม่ทำงานตามมาตรฐานบอกว่าคุณควรจะใช้trap cmd SIGINT trap cmd INT
schily

อ่าใช่ภายใต้ POSIX แน่นอนว่าไม่มี SIG-prefix
phk

อ๊ะ แต่แล้ว "read -p" ก็ไม่ได้รับการสนับสนุนเช่นกันดังนั้นฉันจะปรับมันเพื่อทุบตี
phk

@ schily: ฉันไม่ทราบว่าคุณหมายถึงอะไรกับ "coprocess" แม้ว่า
phk

หน้าคน Korn Shell บอกว่าread -pอ่านข้อมูลจากกระบวนการร่วมปัจจุบัน
schily

คำตอบ:


4

สิ่งที่คุณต้องทำคือเปลี่ยนตัวจัดการ EXIT ภายในตัวจัดการการล้างข้อมูลของคุณ นี่คือตัวอย่าง:

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '

คุณหมายถึงtrap cleanup INTแทนtrap cleanup EXIT?
Jeff Schaller

ฉันคิดว่าฉันหมายถึงออก เมื่อเรียกออกในที่สุดก็เสร็จแล้วเราเปลี่ยนกับดักเพื่อออกด้วยกลับ 0 ฉันไม่เชื่อว่าตัวจัดการสัญญาณซ้ำ
rocky

ตกลงในตัวอย่างของคุณฉันไม่ได้ดัก (SIG) INT เลยซึ่งเป็นสิ่งที่ฉันต้องการเพื่อทำความสะอาดแม้ว่าผู้ใช้กำลังออกจากสคริปต์การโต้ตอบผ่าน ctrl-c
phk

@phk Ok ฉันคิดว่าแม้ว่าคุณจะได้รับความคิดของวิธีการบังคับให้ออกเพื่อกลับ 0 หรือค่าอื่น ๆ ซึ่งสิ่งที่ฉันรวบรวมเป็นปัญหา ฉันคิดว่าคุณจะสามารถปรับรหัสเพื่อให้การตั้งค่า EXIT ของแทร็บในตัวจัดการการล้างข้อมูลจริงของคุณซึ่งถูกเรียกโดย SIGINT
rocky

@ ร็อคกี้: เป้าหมายของฉันคือมีตัวจัดการกับดักในขณะที่ไม่เปลี่ยนรหัสออกที่ปกติฉันจะไม่มีหากใช้ตัวจัดการ ตอนนี้ฉันสามารถคืนรหัสทางออกที่ปกติคุณจะได้รับ แต่ปัญหาคือฉันไม่ทราบรหัสทางออกที่แน่นอนในกรณีเหล่านี้ (ดังที่เห็นในเธรดที่ฉันเชื่อมโยงและโพสต์จาก meuh มันค่อนข้างซับซ้อน) โพสต์ของคุณยังคงมีประโยชน์ฉันไม่คิดเกี่ยวกับการกำหนดตัวจัดการกับดักแบบไดนามิกอีกครั้ง
phk

9

ที่จริงแล้วการขัดจังหวะภายในของ bash นั้นreadดูเหมือนจะแตกต่างกันเล็กน้อยในการขัดจังหวะคำสั่งที่ดำเนินการโดย bash โดยปกติเมื่อคุณป้อนtrap, $?การตั้งค่าและคุณสามารถรักษามันไว้และออกที่มีมูลค่าเท่ากัน:

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

หากสคริปต์ของคุณถูกขัดจังหวะเมื่อเรียกใช้คำสั่งเช่นsleep หรือแม้แต่บิวอินเช่นwaitคุณจะเห็น

130 SIGINT
130 EXIT

และรหัสทางออกคือ 130 อย่างไรก็ตามสำหรับread -pดูเหมือนว่า$?จะเป็น 0 (สำหรับเวอร์ชั่นของทุบตี 4.3.42 ต่อไป)


การจัดการสัญญาณในระหว่างreadอาจกำลังดำเนินการตามไฟล์การเปลี่ยนแปลงในรุ่นของฉัน ... (/ usr / share / doc / bash / CHANGES)

การเปลี่ยนแปลงระหว่างรุ่นนี้ bash-4.3-alpha และรุ่นก่อนหน้า bash-4.2-release

  1. คุณสมบัติใหม่ใน Bash

    R เมื่ออยู่ในโหมด Posix `read 'จะถูกขัดจังหวะด้วยสัญญาณที่ติดอยู่ หลังจากรันตัวจัดการกับดักการอ่านจะส่งสัญญาณ 128 + และทิ้งอินพุตที่อ่านได้บางส่วน


ตกลงมันแปลกจริงๆ มันคือ 130 บนเชลล์แบบโต้ตอบบน bash 4.3.42 (ภายใต้ cygwin), 0 ภายใต้เชลล์เดียวกันเมื่ออยู่ในสคริปต์และโหมด POSIX หรือไม่สร้างความแตกต่าง แต่ภายใต้เส้นประและช่องว่างเป็นเสมอ 1
phk

ฉันลองใช้โหมด POSIX ด้วยกับดักที่ไม่ออกและเริ่มใหม่readตามที่ระบุในไฟล์ CHANGES (เพิ่มในคำตอบของฉัน) ดังนั้นอาจกำลังดำเนินการอยู่
meuh

รหัสทางออก 130 ไม่ได้พกพาได้ 100% ในขณะที่บอร์นเชลล์ใช้128 + signoเป็นรหัสที่ออกสำหรับสัญญาณ ksh93 256 + signoใช้ POSIX กล่าวว่าบางสิ่งบางอย่างข้างต้น 128 ....
schily

@schily True ตามที่ระบุไว้ในเธรดที่ฉันเชื่อมโยงกับในโพสต์ดั้งเดิม ( unix.stackexchange.com/questions/99112 )
phk

6

รหัสทางออกสัญญาณใด ๆ ตามปกติจะมีให้$?เมื่อเข้าสู่ตัวจัดการกับดัก:

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

หากมีกับดัก EXIT แยกต่างหากคุณสามารถใช้วิธีการเดียวกัน: รักษาสถานะทางออกที่ส่งผ่านจากตัวจัดการสัญญาณ (ถ้ามี) ล้างข้อมูลจากนั้นส่งคืนสถานะทางออกที่บันทึกไว้


สิ่งนี้ใช้กับหอยส่วนใหญ่หรือไม่ ดีมาก. localไม่ใช่ POSIX
phk

1
แก้ไข ฉันไม่ได้ผ่านการทดสอบในอื่น ๆ {ba,z}shกว่า AFAIK เป็นสิ่งที่ดีที่สุดที่สามารถทำได้
Tom Hale

สิ่งนี้ไม่ทำงานในการประ ( $?เป็น 1 สิ่งที่สัญญาณที่ได้รับ)
MoonSweep

2

เพียงส่งคืนรหัสข้อผิดพลาดบางอย่างไม่เพียงพอที่จะจำลองการออกโดย SIGINT ฉันประหลาดใจที่ไม่มีใครพูดถึงเรื่องนี้ อ่านเพิ่มเติม: https://www.cons.org/cracauer/sigint.html

วิธีที่เหมาะสมคือ:

for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
    trap "cleanup;
          [ $sig  = EXIT ] && normal_exit_only_cleanup;
          [ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
         " $sig
done

ใช้งานได้กับ Bash, Dash และ zsh เพื่อความสะดวกในการพกพาเพิ่มเติมคุณจะต้องใช้ข้อมูลจำเพาะสัญญาณตัวเลข (ในทางกลับกัน zsh ต้องการพารามิเตอร์สตริงสำหรับkillคำสั่ง ... )

นอกจากนี้ยังสังเกตการรักษาพิเศษของEXITสัญญาณ นี่เป็นเพราะเชลล์บางตัว (คือ Bash) ที่ดำเนินการกับดักEXITสำหรับสัญญาณใด ๆ เช่นกัน การรีเซ็ตของEXITกับดักป้องกันไม่ให้

การใช้รหัสในการออก "ปกติ" เท่านั้น

การตรวจสอบ[ $sig = EXIT ]อนุญาตให้เรียกใช้งานโค้ดในการออกปกติ (ไม่ใช่สัญญาณ) เท่านั้น อย่างไรก็ตามสัญญาณทั้งหมดจะต้องมีกับดักที่ในที่สุดรีเซ็ตกับดักEXITแล้ว; normal_exit_only_cleanupจะถูกเรียกสำหรับสัญญาณที่ทำไม่ได้ด้วย set -eนอกจากนี้ยังจะดำเนินการโดยการออกผ่าน นี้สามารถแก้ไขได้โดยการวางกับดักบนERR(ซึ่งไม่ได้รับการสนับสนุนโดย Dash) และการเพิ่มการตรวจสอบก่อน[ $sig = ERR ]kill

รุ่น Bash-only แบบง่าย

ในทางตรงกันข้ามพฤติกรรมนี้หมายความว่าใน Bash คุณสามารถทำได้

trap cleanup EXIT

เพื่อเรียกใช้งานโค้ดการล้างข้อมูลและเก็บสถานะการออก

แก้ไข

  • อธิบายพฤติกรรมของ Bash อย่าง "EXIT traps Everything"

  • ลบสัญญาณ KILL ซึ่งไม่สามารถดักจับได้

  • ลบคำนำหน้า SIG ออกจากชื่อสัญญาณ

  • อย่าพยายาม kill -s EXIT

  • พิจารณาset -e/ ERR


ไม่มีข้อกำหนดทั่วไปที่โปรแกรมที่สัญญาณดักสิ้นสุดในทางที่รักษาความจริงที่ว่ามันได้รับสัญญาณ เช่นโปรแกรมอาจประกาศว่าการส่งSIGINTเป็นวิธีปิดและอาจตัดสินใจออกด้วย 0 หากมีการจัดการเพื่อยุติโดยไม่มีข้อผิดพลาดหรือรหัสข้อผิดพลาดอื่นหากไม่ได้ปิดอย่างหมดจด กรณีในจุด: top. เรียกใช้top; echo $?แล้วกด Ctrl-C สถานะที่ถูกทิ้งบนหน้าจอจะเป็น 0
Louis

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