วิธีดักจับข้อผิดพลาดใน Bash Scripts บน Linux
เผยแพร่แล้ว: 2022-08-27ตามค่าเริ่มต้น สคริปต์ทุบตีบน Linux จะรายงานข้อผิดพลาดแต่ยังคงทำงานต่อไป เราแสดงวิธีจัดการกับข้อผิดพลาดด้วยตนเอง เพื่อให้คุณสามารถตัดสินใจได้ว่าจะต้องเกิดอะไรขึ้นต่อไป
การจัดการข้อผิดพลาดในสคริปต์
การจัดการข้อผิดพลาดเป็นส่วนหนึ่งของการเขียนโปรแกรม แม้ว่าคุณจะเขียนโค้ดไร้ที่ติ คุณก็ยังพบเงื่อนไขข้อผิดพลาดได้ สภาพแวดล้อมในคอมพิวเตอร์ของคุณเปลี่ยนแปลงตลอดเวลา เมื่อคุณติดตั้งและถอนการติดตั้งซอฟต์แวร์ สร้างไดเร็กทอรี และดำเนินการอัปเกรดและอัปเดต
ตัวอย่างเช่น สคริปต์ที่เคยทำงานโดยไม่มีปัญหาอาจประสบปัญหาหากเปลี่ยนเส้นทางไดเรกทอรี หรือเปลี่ยนสิทธิ์ในไฟล์ การดำเนินการเริ่มต้นของเชลล์ Bash คือการพิมพ์ข้อความแสดงข้อผิดพลาดและดำเนินการสคริปต์ต่อไป นี่เป็นค่าเริ่มต้นที่อันตราย
หากการดำเนินการที่ล้มเหลวมีความสำคัญต่อการประมวลผลหรือการดำเนินการอื่นๆ ที่เกิดขึ้นภายหลังในสคริปต์ของคุณ การดำเนินการที่สำคัญนั้นจะไม่ประสบความสำเร็จ ความหายนะที่เกิดขึ้นนั้นขึ้นอยู่กับว่าสคริปต์ของคุณพยายามทำอะไร
รูปแบบที่มีประสิทธิภาพมากขึ้นจะตรวจจับข้อผิดพลาดและปล่อยให้สคริปต์ทำงานหากจำเป็นต้องปิดระบบหรือพยายามแก้ไขเงื่อนไขข้อบกพร่อง ตัวอย่างเช่น หากไดเร็กทอรีหรือไฟล์หายไป อาจเป็นการดีที่จะให้สคริปต์สร้างใหม่
หากสคริปต์พบปัญหาที่ไม่สามารถกู้คืนได้ ก็สามารถปิดตัวลงได้ หากสคริปต์ต้องปิดตัวลง อาจมีโอกาสดำเนินการล้างข้อมูลที่จำเป็น เช่น ลบไฟล์ชั่วคราวหรือเขียนเงื่อนไขข้อผิดพลาดและเหตุผลที่ปิดลงในไฟล์บันทึก
การตรวจจับสถานะการออก
คำสั่งและโปรแกรมสร้างค่าที่ส่งไปยังระบบปฏิบัติการเมื่อสิ้นสุดการทำงาน สิ่งนี้เรียกว่าสถานะการออก มีค่าเป็นศูนย์หากไม่มีข้อผิดพลาด หรือมีค่าที่ไม่ใช่ศูนย์หากมีข้อผิดพลาดเกิดขึ้น
เราสามารถตรวจสอบสถานะการออกหรือเรียกอีกอย่างว่ารหัสส่งคืนของคำสั่งที่สคริปต์ใช้ และตรวจสอบว่าคำสั่งนั้นสำเร็จหรือไม่
ใน Bash ศูนย์เท่ากับจริง หากการตอบสนองจากคำสั่งเป็นอย่างอื่นที่ไม่ใช่ความจริง เรารู้ว่ามีปัญหาเกิดขึ้นและเราสามารถดำเนินการตามความเหมาะสม
คัดลอกสคริปต์นี้ไปยังโปรแกรมแก้ไข และบันทึกลงในไฟล์ชื่อ “bad_command.sh”
#!/bin/bash ถ้า ( ! bad_command ); แล้ว echo "bad_command ตรวจพบข้อผิดพลาด" ทางออก 1 fi
คุณจะต้องทำให้สคริปต์ทำงานได้ด้วยคำสั่ง chmod
นี่เป็นขั้นตอนที่จำเป็นในการทำให้สคริปต์ทำงานได้ ดังนั้นหากคุณต้องการลองใช้สคริปต์ในเครื่องของคุณเอง อย่าลืมทำเช่นนี้กับแต่ละสคริปต์ แทนที่ชื่อของสคริปต์ที่เหมาะสมในแต่ละกรณี
chmod +x bad_command.sh
เมื่อเราเรียกใช้สคริปต์ เราจะเห็นข้อความแสดงข้อผิดพลาดที่คาดไว้
./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
เสียงสะท้อน $?
ฟังก์ชัน 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
ช่วยให้แน่ใจว่าการตรวจจับข้อผิดพลาดและการดักจับทำงานในฟังก์ชันเชลล์
หากต้องการจับตัวแปรที่ยังไม่ได้กำหนดค่า ให้เพิ่มตัวเลือก -u
(unset) เพื่อให้แน่ใจว่าตรวจพบข้อผิดพลาดในลำดับการวางท่อ ให้เพิ่มตัวเลือก -o pipefail
หากไม่มีสิ่งนี้ สถานะการออกของลำดับไพพ์ของคำสั่งจะเป็นสถานะการออกของคำสั่ง สุดท้าย ในลำดับ คำสั่งที่ล้มเหลวตรงกลางลำดับไปป์จะไม่ถูกตรวจพบ ตัวเลือก -o pipefail
ต้องอยู่ในรายการตัวเลือก
ลำดับที่จะเพิ่มที่ด้านบนสุดของสคริปต์ของคุณคือ:
ชุด -Eeuo pipefail
นี่คือสคริปต์สั้นๆ ที่เรียกว่า “unset-var.sh” โดยมีตัวแปรที่ไม่ได้ตั้งค่าอยู่ในนั้น
#!/bin/bash ชุด -Eou pipefail echo "$unset_variable" echo "เราเห็นบรรทัดนี้หรือไม่"
เมื่อเรารันสคริปต์ unset_variable จะถูกจดจำเป็นตัวแปรที่ยังไม่ได้กำหนดค่าเริ่มต้น และสคริปต์จะถูกยกเลิก
./unset-var.sh
คำสั่ง 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
เราสามารถใช้ 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
ฟังก์ชัน error_handler
ของเราจะแสดงรายละเอียดของข้อผิดพลาดไปที่หน้าต่างเทอร์มินัล หากต้องการ คุณสามารถเพิ่มคำสั่ง exit
ให้กับฟังก์ชันเพื่อให้สคริปต์ยุติการทำงานได้ หรือคุณสามารถใช้ชุดคำสั่ง if/elif/fi
เพื่อดำเนินการต่าง ๆ สำหรับข้อผิดพลาดที่แตกต่างกัน
อาจแก้ไขข้อผิดพลาดบางอย่างได้ บางอย่างอาจต้องการให้สคริปต์หยุดทำงาน
เคล็ดลับสุดท้าย
การตรวจจับข้อผิดพลาดมักหมายถึงการสำรองสิ่งที่อาจผิดพลาดไว้ล่วงหน้า และใส่โค้ดเพื่อจัดการกับเหตุการณ์ที่เกิดขึ้น นอกเหนือจากการตรวจสอบให้แน่ใจว่าขั้นตอนการดำเนินการและตรรกะภายในของสคริปต์ของคุณถูกต้องแล้ว
หากคุณใช้คำสั่งนี้เพื่อเรียกใช้สคริปต์ของคุณ Bash จะแสดงผลลัพธ์การติดตามเมื่อสคริปต์ดำเนินการ:
bash -x your-script.sh
Bash เขียนเอาต์พุตการติดตามในหน้าต่างเทอร์มินัล มันแสดงแต่ละคำสั่งพร้อมอาร์กิวเมนต์—ถ้ามี สิ่งนี้เกิดขึ้นหลังจากขยายคำสั่งแต่ก่อนที่จะดำเนินการ
มันสามารถช่วยได้มากในการติดตามข้อบกพร่องที่เข้าใจยาก
ที่เกี่ยวข้อง: วิธีตรวจสอบไวยากรณ์ของสคริปต์ Linux Bash ก่อนใช้งาน