ป้ายกำกับ: Features

รวมเทคนิคเขียน Shell Script แบบ Defensive Programmingรวมเทคนิคเขียน Shell Script แบบ Defensive Programming

การเขียน Shell Script สไตล์ Defensive Programming คือการตั้งสมมติฐานไว้ล่วงหน้าว่า ทุกอินพุตสามารถผิดพลาดได้ ทุกคำสั่งมีโอกาสล้มเหลว และสภาพแวดล้อมภายนอกไม่เสถียร เป้าหมายหลักคือการหยุดการทำงานทันทีก่อนที่ข้อผิดพลาดจะลุกลามจนสร้างความเสียหายต่อระบบ (Fail-Fast and Safe)


1. ล็อกโหมดความปลอดภัยที่หัวไฟล์เสมอ (Strict Flags)

บรรทัดแรกหลัง Shebang ควรเปิดการทำงานของแฟล็กความปลอดภัย

BASH / Shell

#!/bin/sh
set -eu
  • 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 &#x5B; -d "$target_cache" ] && &#x5B; "$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

5. ควบคุมสภาพแวดล้อม (Environment Isolation)

1. กำหนด PATH ให้ชัดเจน ป้องกัน Path Hijacking

BASH / Shell

# ล็อก PATH ให้ค้นหาในไดเรกทอรีมาตรฐานก่อนเสมอ
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH

2. ฟิกซ์ Locale ป้องกันพฤติกรรมการ Sort หรือ Regex เพี้ยน

BASH / Shell

# บังคับใช้ ASCII/C Standard เพื่อให้ผลลัพธ์ของ sort, grep, sed เหมือนกันทุกเซิร์ฟเวอร์
export LC_ALL=C

3. รีเซ็ต IFS ป้องกันการแตกคำเพี้ยน

BASH / Shell

# คืนค่า Internal Field Separator เป็น Space, Tab, Newline มาตรฐาน
IFS=' 	
'