“ กับดัก…ออกจากระยะเวลา” จำเป็นจริงๆหรือ?


63

ตัวอย่างมากมายสำหรับtrapใช้trap ... INT TERM EXITสำหรับงานการล้างข้อมูล แต่มันจำเป็นจริงๆหรือไม่ที่จะแสดงรายการทั้งสาม sigspecs?

คู่มือบอกว่า:

หาก SIGNAL_SPEC เป็น EXIT (0) ARG จะถูกดำเนินการเมื่อออกจากเปลือก

ซึ่งผมเชื่อว่าไม่ว่าจะใช้สคริปต์เสร็จได้ตามปกติหรือมันเสร็จเพราะมันได้รับหรือSIGINT SIGTERMการทดลองยืนยันความเชื่อของฉันด้วย:

$ 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

แล้วทำไมตัวอย่างจำนวนมากดังนั้นรายการทั้งหมดของINT TERM EXIT? หรือว่าฉันพลาดอะไรบางอย่างและมีกรณีใดที่คนเดียวEXITจะพลาด?


3
นอกจากนี้โปรดทราบว่าด้วยสเปคเช่นINT TERM EXITรหัสการล้างข้อมูลจะถูกดำเนินการสองครั้งเมื่อSIGTERMหรือSIGINTได้รับ
maxschlepzig

คำตอบ:


19

ข้อมูลจำเพาะ POSIXไม่พูดมากเกี่ยวกับเงื่อนไขที่เกิดขึ้นในการดำเนินการกับดัก EXIT เพียงเกี่ยวกับสิ่งที่สภาพแวดล้อมของมันจะต้องมีลักษณะเช่นเมื่อมีการดำเนินการ

ในเปลือกขี้เถ้าของ Busybox การทดสอบกับดักของคุณไม่ได้สะท้อน 'TRAP' ก่อนออกจากเนื่องจาก SIGINT หรือ SIGTERM ฉันสงสัยว่ามีกระสุนอื่น ๆ ที่อยู่ในตำแหน่งที่ไม่สามารถใช้งานได้เช่นกัน

# /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
dashยังไม่ได้ดักเพียงเมื่อได้รับEXIT SIGINT/SIGTERM
maxschlepzig

4
zshเช่นกัน - ดังนั้นบางทีอาจbashเป็นเชลล์เดียวที่EXITยังจับคู่สัญญาณ
maxschlepzig

@maxschlepzig zshไม่ได้กับดักในEXITเมื่อได้รับแต่ไม่เมื่อได้รับINT TERMแก้ไข: ฉันเพิ่งสังเกตเห็นว่าอายุเท่าไร ...
JoL

27

ใช่มีความแตกต่าง

สคริปต์นี้จะออกเมื่อคุณกดEnterหรือส่งSIGINTหรือSIGTERM:

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

สคริปต์นี้จะออกเมื่อคุณกดEnter:

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

* ผ่านการทดสอบในการดวลจุดโทษ , ทุบตีและzsh (ไม่ทำงานในshเมื่อคุณเพิ่มคำสั่งเพื่อให้กับดักทำงาน)


นอกจากนี้ยังมีสิ่งที่ @Shawn กล่าวว่า: แอชและรีบEXITทำไม่ได้ส่งสัญญาณกับดักกับ

ดังนั้นในการจัดการสัญญาณอย่างแน่นหนาวิธีที่ดีที่สุดคือหลีกเลี่ยงการดักEXITรวมกันและใช้สิ่งนี้:

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

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

1
การแก้ปัญหาด้วยการทำความสะอาดนั้นเป็นสิ่งที่ถูกต้อง - สง่างามมาก! มันกลายเป็นสำนวนสำหรับสคริปต์ทุบตีของฉันด้วยการmktempโทร
Bjoern Dahlgren

2
มีexitความจำเป็นcleanupหรือไม่
jarno

3
วิธีนี้ใช้ไม่ได้หากคุณมีข้อผิดพลาด shellscript ในรหัสของคุณซึ่งทำให้เกิดการออกก่อนเวลาอันควร
ijw

2
@ijw: ใน Bash และ Ksh คุณสามารถดักERRจับมันได้แต่มันไม่สามารถพกพาได้
Zaz

5
โซลูชันนี้ไม่แข็งแกร่งเมื่อเชลล์อื่นเรียกว่า มันไม่ได้จัดการกับรอเมื่อออกจากสหกรณ์ ; คุณจะต้องการtrap - INT TERM; kill -2 $$เป็นบรรทัดสุดท้ายของการล้างข้อมูลเพื่อบอกเชลล์พาเรนต์ว่ามันออกก่อนเวลาอันควร ถ้า parent parent foobar.sh เรียกสคริปต์ของคุณ (foo.sh) แล้วเรียก bar.sh คุณไม่ต้องการให้ bar.sh ประมวลผลถ้า INT / TERM ถูกส่งไปยัง foo.sh ของคุณ trap cleanup EXITจะจัดการการเผยแพร่นี้โดยอัตโนมัติดังนั้น IMO จึงแข็งแกร่งที่สุด นอกจากนี้ยังหมายความว่าคุณจะไม่ต้องโทรcleanupเมื่อสิ้นสุดสคริปต์
Nicholas Pipitone

12

ปรับแก้คำตอบสุดท้ายเนื่องจากมีปัญหา:

# 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

คะแนนด้านบน:

ตัวจัดการ INT และ TERM จะไม่ออกจากฉันเมื่อฉันทดสอบ - พวกเขาจัดการกับข้อผิดพลาดจากนั้นเชลล์จะกลับไปที่การออก (และนี่ก็ไม่น่าแปลกใจเกินไป) ดังนั้นฉันจึงมั่นใจได้ว่าการล้างข้อมูลจะออกหลังจากนั้นและในกรณีที่สัญญาณใช้รหัสข้อผิดพลาดเสมอ (และในกรณีอื่น ๆ ของทางออกปกติให้เก็บรหัสข้อผิดพลาดไว้)

ด้วย bash ดูเหมือนว่าการออกจาก INT handler นั้นเรียกว่า EXIT handler ด้วยเหตุนี้ฉันจึงแกะตัวจัดการทางออกและเรียกมันว่าตัวเอง (ซึ่งจะทำงานในเชลล์ใด ๆ โดยไม่คำนึงถึงพฤติกรรม)

ฉันดักออกเพราะเชลล์สคริปต์สามารถออกก่อนที่พวกเขาจะมาถึงด้านล่าง - ข้อผิดพลาดทางไวยากรณ์ตั้ง -e และกลับไม่ใช่ศูนย์เพียงโทรออก คุณไม่สามารถพึ่งพา shellscript ที่อยู่ด้านล่าง

SIGQUIT คือ Ctrl- \ หากคุณไม่เคยลอง ทำให้คุณได้รับโบนัส coredump ดังนั้นฉันคิดว่ามันคุ้มค่ากับการดักแม้ว่ามันจะคลุมเครือเล็กน้อย

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


2
โทรก็จะได้รับ 1 เป็นรหัสที่ออกไม่ว่าสิ่งที่ก่อให้เกิดสัญญาณทางออกในขณะที่ withour trapโทรจะได้รับ 130 SIGINT 143 สำหรับ SIGTERM ฯลฯ sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }ดังนั้นฉันจะจับและส่งรหัสทางออกที่ถูกต้อง:
musiphil

2
คุณช่วยอธิบายจุดประสงค์ของtrap '' EXIT INT TERMฟังก์ชั่นล้างข้อมูลได้หรือไม่? นี่เป็นการป้องกันผู้ใช้โดยไม่ได้ตั้งใจจากการล้างข้อมูลที่คุณกล่าวถึงในย่อหน้าสุดท้ายหรือไม่ ไม่EXITซ้ำซ้อนหรือไม่
หก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.