วิธีดักจับข้อผิดพลาดใน 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 ก่อนใช้งาน


