ง่ายต่อการตรวจจับเมื่อเชลล์สคริปต์เริ่มทำงาน แต่ก็ไม่ง่ายเสมอไปที่จะรู้ว่าเมื่อใดที่เชลล์สคริปต์เริ่มทำงาน สคริปต์อาจจบลงตามปกติ เช่นเดียวกับที่ผู้เขียนตั้งใจให้สคริปต์จบ แต่ก็อาจล้มเหลวได้เช่นกันเนื่องจากข้อผิดพลาดร้ายแรงที่ไม่คาดคิด บางครั้งก็เป็นประโยชน์ที่จะรักษาส่วนที่เหลือของสิ่งที่อยู่ระหว่างดำเนินการเมื่อสคริปต์ล้มเหลว และในบางครั้งอาจไม่สะดวก ไม่ว่าจะด้วยวิธีใด การตรวจจับจุดสิ้นสุดของสคริปต์และตอบสนองต่อมันในลักษณะที่คำนวณไว้ล่วงหน้าเป็นสาเหตุที่ทำให้ Bash trap
มีคำสั่งอยู่
ตอบสนองต่อความล้มเหลว
ต่อไปนี้คือตัวอย่างว่าความล้มเหลวในสคริปต์สามารถนำไปสู่ความล้มเหลวในอนาคตได้อย่างไร สมมติว่าคุณเขียนโปรแกรมที่สร้างไดเร็กทอรีชั่วคราวใน /tmp
เพื่อให้สามารถยกเลิกการเก็บถาวรและประมวลผลไฟล์ก่อนที่จะรวมเข้าด้วยกันในรูปแบบอื่น:
#!/usr/bin/env bash
CWD=`pwd`
TMP=${TMP:-/tmp/tmpdir}
## create tmp dir
mkdir "${TMP}"
## extract files to tmp
tar xf "${1}" --directory "${TMP}"
## move to tmpdir and run commands
pushd "${TMP}"
for IMG in *.jpg; do
mogrify -verbose -flip -flop "${IMG}"
done
tar --create --file "${1%.*}".tar *.jpg
## move back to origin
popd
## bundle with bzip2
bzip2 --compress "${TMP}"/"${1%.*}".tar \
--stdout > "${1%.*}".tbz
## clean up
/usr/bin/rm -r /tmp/tmpdir
โดยส่วนใหญ่ สคริปต์จะทำงานตามที่คาดไว้ อย่างไรก็ตาม หากคุณเผลอเรียกใช้ไฟล์ดังกล่าวในไฟล์เก็บถาวรซึ่งเต็มไปด้วยไฟล์ PNG แทนที่จะเป็นไฟล์ JPEG ที่คาดไว้ ไฟล์ดังกล่าวจะล้มเหลวครึ่งทาง ความล้มเหลวหนึ่งนำไปสู่อีกความล้มเหลวหนึ่ง และในที่สุด สคริปต์จะออกจากการทำงานโดยไม่ได้รับคำสั่งสุดท้ายในการลบไดเร็กทอรีชั่วคราว ตราบใดที่คุณลบไดเร็กทอรีด้วยตนเอง คุณสามารถกู้คืนได้อย่างรวดเร็ว แต่ถ้าคุณไม่พร้อมจะทำอย่างนั้น ในครั้งต่อไปที่สคริปต์ทำงาน สคริปต์จะต้องจัดการกับไดเร็กทอรีชั่วคราวที่มีอยู่ซึ่งเต็มไปด้วยไฟล์ที่เหลือที่คาดเดาไม่ได้
วิธีหนึ่งในการต่อสู้กับสิ่งนี้คือการย้อนกลับและเพิ่มตรรกะเป็นสองเท่าโดยเพิ่มการลบด้วยความระมัดระวังในช่วงเริ่มต้นของสคริปต์ แม้ว่าจะถูกต้อง แต่ก็ต้องอาศัยกำลังเดรัจฉานแทนโครงสร้าง โซลูชันที่หรูหรากว่าคือ trap
.
จับสัญญาณด้วยกับดัก
trap
คีย์เวิร์ดจับ สัญญาณ ที่อาจเกิดขึ้นระหว่างดำเนินการ คุณเคยใช้หนึ่งในสัญญาณเหล่านี้ หากคุณเคยใช้ kill
หรือ killall
คำสั่งซึ่งเรียก SIGTERM
โดยค่าเริ่มต้น. มีสัญญาณอื่นๆ อีกมากมายที่เชลล์ตอบสนอง และคุณสามารถเห็นสัญญาณส่วนใหญ่ด้วย trap -l
(ดังใน "รายการ"):
$ trap --list
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
สัญญาณใด ๆ เหล่านี้สามารถคาดการณ์ได้ด้วย trap
. นอกจากนี้ trap
รับรู้:
EXIT
:เกิดขึ้นเมื่อกระบวนการเชลล์ออกเองERR
:เกิดขึ้นเมื่อคำสั่ง (เช่น tar หรือ mkdir ) หรือคำสั่งในตัว (เช่น พุช หรือ cd ) เสร็จสมบูรณ์ด้วยสถานะที่ไม่ใช่ศูนย์DEBUG
:บูลีนแสดงโหมดดีบัก
ในการตั้งกับดักใน Bash ให้ใช้ trap
ตามด้วยรายการคำสั่งที่คุณต้องการดำเนินการ ตามด้วยรายการสัญญาณที่จะเรียกใช้
ตัวอย่างเช่น กับดักนี้ตรวจพบ SIGINT
, สัญญาณที่ส่งเมื่อผู้ใช้กด Ctrl+C ในขณะที่กระบวนการกำลังทำงาน:
trap "{ echo 'Terminated with Ctrl+C'; }" SIGINT
สคริปต์ตัวอย่างที่มีปัญหาไดเรกทอรีชั่วคราวสามารถแก้ไขได้ด้วยการตรวจจับกับดัก SIGINT
, ข้อผิดพลาด และการออกที่สำเร็จ:
#!/usr/bin/env bash
CWD=`pwd`
TMP=${TMP:-/tmp/tmpdir}
trap \
"{ /usr/bin/rm -r "${TMP}" ; exit 255; }" \
SIGINT SIGTERM ERR EXIT
## create tmp dir
mkdir "${TMP}"
tar xf "${1}" --directory "${TMP}"
## move to tmp and run commands
pushd "${TMP}"
for IMG in *.jpg; do
mogrify -verbose -flip -flop "${IMG}"
done
tar --create --file "${1%.*}".tar *.jpg
## move back to origin
popd
## zip tar
bzip2 --compress $TMP/"${1%.*}".tar \
--stdout > "${1%.*}".tbz
สำหรับการดำเนินการที่ซับซ้อน คุณสามารถลดความซับซ้อนของ trap
คำสั่งที่มีฟังก์ชัน Bash
กับดักใน Bash
กับดักมีประโยชน์เพื่อให้แน่ใจว่าสคริปต์ของคุณสิ้นสุดโดยสมบูรณ์ ไม่ว่าจะทำงานสำเร็จหรือไม่ก็ตาม มันไม่ปลอดภัยที่จะพึ่งพาการรวบรวมขยะอัตโนมัติทั้งหมด ดังนั้นนี่จึงเป็นนิสัยที่ดีที่ควรทำโดยทั่วไป ลองใช้มันในสคริปต์ของคุณ และดูว่ามันทำอะไรได้บ้าง!