การเขียน Shell Script สไตล์ Defensive Programming คือการตั้งสมมติฐานไว้ล่วงหน้าว่า ทุกอินพุตสามารถผิดพลาดได้ ทุกคำสั่งมีโอกาสล้มเหลว และสภาพแวดล้อมภายนอกไม่เสถียร เป้าหมายหลักคือการหยุดการทำงานทันทีก่อนที่ข้อผิดพลาดจะลุกลามจนสร้างความเสียหายต่อระบบ (Fail-Fast and Safe)
1. ล็อกโหมดความปลอดภัยที่หัวไฟล์เสมอ (Strict Flags)
บรรทัดแรกหลัง Shebang ควรเปิดการทำงานของแฟล็กความปลอดภัย
BASH / Shell
set -e (Exit on Error)
หยุดสคริปต์ทันทีเมื่อคำสั่งใดคำสั่งหนึ่งคืนค่า Exit code ที่ไม่ใช่ 0
set -u (No Unset)
แจ้ง Error และหยุดทันทีหากมีการเรียกใช้ตัวแปรที่ยังไม่ได้กำหนดค่า ป้องกันอุบัติเหตุ rm -rf "$TARGET/" เมื่อ $TARGET ว่างเปล่า
หากใช้ Bash: แนะนำให้ใช้ set -euo pipefail เพิ่มเติม
-o pipefail
ถ้าคำสั่งใดในไปป์ (cmd1 | cmd2 | cmd3) พัง ตัวสคริปต์จะมองว่าทั้งไปป์นั้นล้มเหลวทันที (ปกติจะดูแค่ exit code ของคำสั่งสุดท้าย)
2. กำหนดค่าตัวแปรอย่างรัดกุม (Safe Parameter Expansion)
อย่าอ้างถึงตัวแปรลอย ๆ ให้ใช้กลไก Parameter Expansion ควบคุมค่าเริ่มต้นและตรวจความถูกต้อง
BASH / Shell
# 1. บังคับว่าต้องมีค่า (Mandatory) หากไม่มีจะหยุดการทำงานพร้อมพิมพ์ error
target_dir="${1:?Error: Target directory parameter is required.}"
# 2. กำหนดค่าเริ่มต้น (Default value) หากไม่มีค่า ให้ใช้ค่าสำรอง
backup_retention="${BACKUP_DAYS:-7}"
# 3. กำหนดค่าและเซ็ตกลับลงตัวแปรหากยังไม่มีค่า (Assign default)
: "${CONFIG_FILE:=/etc/app/config.json}"
3. เทคนิคป้องกันหายนะคำสั่ง rm -rf
คำสั่งลบไฟล์เป็นจุดที่อันตรายที่สุด ให้ตรวจสอบอย่างน้อย 2 ชั้นก่อนลบเสมอ
BASH / Shell
# อันตรายมาก (ห้ามเขียนเด็ดขาด):
# rm -rf "$cache_dir/*" <-- หาก cache_dir ว่างเปล่า จะกลายเป็น rm -rf /*
# วิธีปลอดภัยระดับ Defensive:
target_cache="${CACHE_DIR:?Error: CACHE_DIR is unset}"
# ตรวจสอบว่าเป็น Directory และไม่ใช่ Root path ก่อนลบ
if [ -d "$target_cache" ] && [ "$target_cache" != "/" ]; then
rm -rf -- "${target_cache:?}"/*
else
printf "Error: Target directory '%s' is invalid or not found.\n" "$target_cache" >&2
exit 1
fi
- เครื่องหมาย
-- ป้องกันไม่ให้ชื่อไฟล์ที่ขึ้นต้นด้วยขีด (-rf, -i) ถูกมองเป็นแฟล็กของคำสั่ง
4. จัดการ Clean up ทรัพยากรด้วย trap
ไม่ว่าสคริปต์จะทำงานจบตามปกติ เจอ Error (set -e) หรือถูกผู้ใช้กดยกเลิก (Ctrl+C), ทรัพยากรชั่วคราว (Temp files, Locks) ต้องถูกเคลียร์ทิ้งเสมอ
BASH / Shell
# สร้าง Temp directory แบบปลอดภัย
tmp_dir="$(mktemp -d)"
cleanup() {
exit_code=$?
# ลบไดเรกทอรีชั่วคราว
rm -rf -- "$tmp_dir"
printf "Cleaned up temporary files.\n" >&2
exit "$exit_code"
}
# ดักจับสัญญาณ EXIT (จบงาน), INT (SIGINT / Ctrl+C), TERM (SIGTERM / Kill)
trap cleanup EXIT INT TERM