Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> การเขียนโปรแกรม BASH

การใช้ Bash traps ในสคริปต์ของคุณ

ง่ายต่อการตรวจจับเมื่อเชลล์สคริปต์เริ่มทำงาน แต่ก็ไม่ง่ายเสมอไปที่จะรู้ว่าเมื่อใดที่เชลล์สคริปต์เริ่มทำงาน สคริปต์อาจจบลงตามปกติ เช่นเดียวกับที่ผู้เขียนตั้งใจให้สคริปต์จบ แต่ก็อาจล้มเหลวได้เช่นกันเนื่องจากข้อผิดพลาดร้ายแรงที่ไม่คาดคิด บางครั้งก็เป็นประโยชน์ที่จะรักษาส่วนที่เหลือของสิ่งที่อยู่ระหว่างดำเนินการเมื่อสคริปต์ล้มเหลว และในบางครั้งอาจไม่สะดวก ไม่ว่าจะด้วยวิธีใด การตรวจจับจุดสิ้นสุดของสคริปต์และตอบสนองต่อมันในลักษณะที่คำนวณไว้ล่วงหน้าเป็นสาเหตุที่ทำให้ 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

กับดักมีประโยชน์เพื่อให้แน่ใจว่าสคริปต์ของคุณสิ้นสุดโดยสมบูรณ์ ไม่ว่าจะทำงานสำเร็จหรือไม่ก็ตาม มันไม่ปลอดภัยที่จะพึ่งพาการรวบรวมขยะอัตโนมัติทั้งหมด ดังนั้นนี่จึงเป็นนิสัยที่ดีที่ควรทำโดยทั่วไป ลองใช้มันในสคริปต์ของคุณ และดูว่ามันทำอะไรได้บ้าง!