วิธีดักจับข้อผิดพลาดใน Bash Scripts บน Linux

เผยแพร่แล้ว: 2022-08-27
แล็ปท็อป Linux แสดง bash prompt
fatmawati achmad zaenuri/Shutterstock.com

ตามค่าเริ่มต้น สคริปต์ทุบตีบน Linux จะรายงานข้อผิดพลาดแต่ยังคงทำงานต่อไป เราแสดงวิธีจัดการกับข้อผิดพลาดด้วยตนเอง เพื่อให้คุณสามารถตัดสินใจได้ว่าจะต้องเกิดอะไรขึ้นต่อไป

การจัดการข้อผิดพลาดในสคริปต์

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

การกำหนดข้อผิดพลาดในการตรวจสอบความถูกต้องของพารามิเตอร์ใน PowerShell
ข้อผิดพลาดในการตรวจสอบความถูกต้องของพารามิเตอร์เอง ที่เกี่ยวข้อง ใน PowerShell

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

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

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

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

การตรวจจับสถานะการออก

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

เราสามารถตรวจสอบสถานะการออกหรือเรียกอีกอย่างว่ารหัสส่งคืนของคำสั่งที่สคริปต์ใช้ และตรวจสอบว่าคำสั่งนั้นสำเร็จหรือไม่

ใน Bash ศูนย์เท่ากับจริง หากการตอบสนองจากคำสั่งเป็นอย่างอื่นที่ไม่ใช่ความจริง เรารู้ว่ามีปัญหาเกิดขึ้นและเราสามารถดำเนินการตามความเหมาะสม

คัดลอกสคริปต์นี้ไปยังโปรแกรมแก้ไข และบันทึกลงในไฟล์ชื่อ “bad_command.sh”

 #!/bin/bash

ถ้า ( ! bad_command ); แล้ว
  echo "bad_command ตรวจพบข้อผิดพลาด"
  ทางออก 1
fi

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

 chmod +x bad_command.sh 

ทำให้สคริปต์สามารถเรียกใช้งานได้โดยใช้ chmod

เมื่อเราเรียกใช้สคริปต์ เราจะเห็นข้อความแสดงข้อผิดพลาดที่คาดไว้

 ./bad_command.sh 

การตรวจสอบสถานะการออกของคำสั่งเพื่อดูว่ามีข้อผิดพลาดหรือไม่

ไม่มีคำสั่งเช่น "bad_command" และไม่ใช่ชื่อของฟังก์ชันภายในสคริปต์ ไม่สามารถดำเนินการได้ ดังนั้นการตอบสนองจึง ไม่ เป็นศูนย์ ถ้าการตอบสนองไม่ใช่ศูนย์—จะใช้เครื่องหมายอัศเจรีย์ที่นี่เป็นตัวดำเนินการ NOT แบบลอจิคัล—เนื้อความของคำสั่ง if จะถูกดำเนินการ

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

อาจดูเหมือน exit 1 ซ้ำซ้อน ท้ายที่สุด ไม่มีอะไรอื่นในสคริปต์และมันจะยุติอยู่ดี แต่การใช้คำสั่ง exit ทำให้เราสามารถส่งสถานะ exit กลับไปยังเชลล์ได้ หากมีการเรียกสคริปต์ของเราจากภายในสคริปต์ที่สอง สคริปต์ที่สองนั้นจะรู้ว่าสคริปต์นี้พบข้อผิดพลาด

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

 command_1 || command_2

ใช้งานได้เพราะคำสั่งแรกรัน OR คำสั่งที่สอง คำสั่งซ้ายสุดรันก่อน หากสำเร็จ คำสั่งที่สองจะไม่ถูกดำเนินการ แต่ถ้าคำสั่งแรกล้มเหลว คำสั่งที่สองจะถูกดำเนินการ เราสามารถจัดโครงสร้างโค้ดแบบนี้ได้ นี่คือ "ตรรกะ-หรือ./sh"

 #!/bin/bash

error_handler()
{
  echo "ข้อผิดพลาด: ($?) $1"
  ทางออก 1
}

bad_command || error_handler "bad_command ล้มเหลว บรรทัด: ${LINENO}"

เราได้กำหนดฟังก์ชันที่เรียกว่า error_handler สิ่งนี้จะพิมพ์สถานะการออกของคำสั่งที่ล้มเหลวซึ่งอยู่ในตัวแปร $? และบรรทัดข้อความที่ส่งผ่านเมื่อมีการเรียกใช้ฟังก์ชัน ซึ่งอยู่ในตัวแปร $1 ฟังก์ชันนี้ยุติสคริปต์ด้วยสถานะออกหนึ่งสถานะ

สคริปต์พยายามเรียกใช้ bad_command ซึ่งเห็นได้ชัดว่าล้มเหลว ดังนั้นคำสั่งทางด้านขวาของตัวดำเนินการตรรกะ OR || , ถูกดำเนินการ ซึ่งเรียกใช้ฟังก์ชัน error_handler และส่งสตริงที่ตั้งชื่อคำสั่งที่ล้มเหลว และมีหมายเลขบรรทัดของคำสั่งที่ล้มเหลว

เราจะเรียกใช้สคริปต์เพื่อดูข้อความตัวจัดการข้อผิดพลาด จากนั้นตรวจสอบสถานะการออกของสคริปต์โดยใช้เสียงสะท้อน

 ./logical-or.sh
 เสียงสะท้อน $? 

การใช้ตัวดำเนินการ logfical OR เพื่อเรียกตัวจัดการข้อผิดพลาดในสคริปต์

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

สถานะการออกของสคริปต์คือหนึ่ง สถานะทางออก 127 ที่รายงานโดย error_handler หมายถึง "ไม่พบคำสั่ง" หากเราต้องการ เราสามารถใช้สิ่งนั้นเป็นสถานะการออกของสคริปต์โดยส่งต่อไปยังคำสั่ง exit

อีกวิธีหนึ่งคือการขยาย error_handler เพื่อตรวจสอบค่าที่เป็นไปได้ที่แตกต่างกันของสถานะการออกและดำเนินการต่างๆ ตามลำดับ โดยใช้โครงสร้างประเภทนี้:

 exit_code=$?

ถ้า [ $exit_code -eq 1 ]; แล้ว
  echo "ไม่อนุญาตให้ดำเนินการ"

เอลิฟ [ $exit_code -eq 2 ]; แล้ว
  echo "การใช้เชลล์บิวด์อินในทางที่ผิด"
.
.
.
เอลฟ์ [ สถานะ $ -eq 128 ]; แล้ว
  echo "อาร์กิวเมนต์ไม่ถูกต้อง"
fi

การใช้ set To Force a Exit

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

ในการดำเนินการนี้ ให้ใช้คำสั่ง set พร้อมตัวเลือก -e (ข้อผิดพลาด) สิ่งนี้บอกให้สคริปต์ออกเมื่อใดก็ตามที่คำสั่งล้มเหลวหรือส่งคืนรหัสทางออกที่มากกว่าศูนย์ นอกจากนี้ การใช้ตัวเลือก -E ช่วยให้แน่ใจว่าการตรวจจับข้อผิดพลาดและการดักจับทำงานในฟังก์ชันเชลล์

วิธีใช้ set และ pipefail ใน Bash Scripts บน Linux
วิธีใช้ set และ pipefail ใน Bash Scripts บน Linux . ที่เกี่ยวข้อง

หากต้องการจับตัวแปรที่ยังไม่ได้กำหนดค่า ให้เพิ่มตัวเลือก -u (unset) เพื่อให้แน่ใจว่าตรวจพบข้อผิดพลาดในลำดับการวางท่อ ให้เพิ่มตัวเลือก -o pipefail หากไม่มีสิ่งนี้ สถานะการออกของลำดับไพพ์ของคำสั่งจะเป็นสถานะการออกของคำสั่ง สุดท้าย ในลำดับ คำสั่งที่ล้มเหลวตรงกลางลำดับไปป์จะไม่ถูกตรวจพบ ตัวเลือก -o pipefail ต้องอยู่ในรายการตัวเลือก

ลำดับที่จะเพิ่มที่ด้านบนสุดของสคริปต์ของคุณคือ:

 ชุด -Eeuo pipefail

นี่คือสคริปต์สั้นๆ ที่เรียกว่า “unset-var.sh” โดยมีตัวแปรที่ไม่ได้ตั้งค่าอยู่ในนั้น

 #!/bin/bash

ชุด -Eou pipefail

echo "$unset_variable"

echo "เราเห็นบรรทัดนี้หรือไม่"

เมื่อเรารันสคริปต์ unset_variable จะถูกจดจำเป็นตัวแปรที่ยังไม่ได้กำหนดค่าเริ่มต้น และสคริปต์จะถูกยกเลิก

 ./unset-var.sh 

การใช้คำสั่ง set ในสคริปต์เพื่อยุติสคริปต์หากมีข้อผิดพลาดเกิดขึ้น

คำสั่ง echo ที่สองจะไม่ถูกดำเนินการ

การใช้กับดักที่มีข้อผิดพลาด

คำสั่ง Bash trap ให้คุณเสนอชื่อคำสั่งหรือฟังก์ชันที่ควรเรียกเมื่อมีการยกสัญญาณเฉพาะ โดยทั่วไปจะใช้เพื่อจับสัญญาณ เช่น SIGINT ซึ่งจะเพิ่มขึ้นเมื่อคุณกดคีย์ผสม Ctrl+C สคริปต์นี้คือ “signt.sh”

 #!/bin/bash

กับดัก "echo -e '\nยุติโดย Ctrl+c'; exit" SIGINT

เคาน์เตอร์=0

ในขณะที่จริง
ทำ 
  echo "หมายเลขลูป:" $((++ตัวนับ))
  นอน 1
เสร็จแล้ว

คำสั่ง trap ประกอบด้วยคำสั่ง echo และคำสั่ง exit มันจะถูกเรียกใช้เมื่อ SIGINT ถูกยกขึ้น ส่วนที่เหลือของสคริปต์เป็นแบบวนซ้ำ หากคุณเรียกใช้สคริปต์และกด Ctrl+C คุณจะเห็นข้อความจากคำจำกัดความของ trap และสคริปต์จะยุติลง

 ./signt.sh 

การใช้กับดักในสคริปต์เพื่อจับ Ctrl+c

เราสามารถใช้ trap กับสัญญาณ ERR เพื่อตรวจจับข้อผิดพลาดที่เกิดขึ้นได้ สิ่งเหล่านี้สามารถป้อนให้กับคำสั่งหรือฟังก์ชันได้ นี่คือ "trap.sh" เรากำลังส่งการแจ้งเตือนข้อผิดพลาดไปยังฟังก์ชันที่เรียกว่า error_handler

 #!/bin/bash

กับดัก 'error_handler $? $LINENO' ผิดพลาด

error_handler() {
  echo "ข้อผิดพลาด: ($ 1) เกิดขึ้นที่ $2"
}

หลัก() {
  echo "ฟังก์ชันภายใน main()"
  bad_command
  ที่สอง
  ที่สาม
  ออก $?
}

ที่สอง() {
  echo "หลังจากโทรไปที่ main()"
  echo "ฟังก์ชันภายในวินาที ()"
}

ที่สาม() {
  echo "ภายในฟังก์ชันที่สาม ()"
}

หลัก

สคริปต์จำนวนมากอยู่ภายในฟังก์ชัน main ซึ่งเรียกใช้ฟังก์ชันที่ second และ third เมื่อพบข้อผิดพลาด ในกรณีนี้ เนื่องจากไม่มี bad_command คำสั่ง trap จะนำข้อผิดพลาดไปที่ฟังก์ชัน error_handler จะส่งสถานะออกจากคำสั่งที่ล้มเหลวและหมายเลขบรรทัดไปยังฟังก์ชัน error_handler

 ./trap.sh 

การใช้กับดักกับ ERR เพื่อตรวจจับข้อผิดพลาดในสคริปต์

ฟังก์ชัน error_handler ของเราจะแสดงรายละเอียดของข้อผิดพลาดไปที่หน้าต่างเทอร์มินัล หากต้องการ คุณสามารถเพิ่มคำสั่ง exit ให้กับฟังก์ชันเพื่อให้สคริปต์ยุติการทำงานได้ หรือคุณสามารถใช้ชุดคำสั่ง if/elif/fi เพื่อดำเนินการต่าง ๆ สำหรับข้อผิดพลาดที่แตกต่างกัน

อาจแก้ไขข้อผิดพลาดบางอย่างได้ บางอย่างอาจต้องการให้สคริปต์หยุดทำงาน

เคล็ดลับสุดท้าย

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

หากคุณใช้คำสั่งนี้เพื่อเรียกใช้สคริปต์ของคุณ Bash จะแสดงผลลัพธ์การติดตามเมื่อสคริปต์ดำเนินการ:

 bash -x your-script.sh

Bash เขียนเอาต์พุตการติดตามในหน้าต่างเทอร์มินัล มันแสดงแต่ละคำสั่งพร้อมอาร์กิวเมนต์—ถ้ามี สิ่งนี้เกิดขึ้นหลังจากขยายคำสั่งแต่ก่อนที่จะดำเนินการ

มันสามารถช่วยได้มากในการติดตามข้อบกพร่องที่เข้าใจยาก

ที่เกี่ยวข้อง: วิธีตรวจสอบไวยากรณ์ของสคริปต์ Linux Bash ก่อนใช้งาน