สคริปต์นี้เขียนขึ้นตามมาตรฐาน POSIX shell (/bin/sh) โดยใช้วิธีอ่านบรรทัดแรกของไฟล์ (First Line Header) เพื่อดักจับ Shebang ที่ชี้ไปยัง Shell ต่างๆ (เช่น sh, bash, dash, ash, zsh หรือ /usr/bin/env sh) แม้ว่าไฟล์นั้นจะไม่มีนามสกุล .sh เช่นไฟล์ในไดเรกทอรี bin/ หรือ CLI tools
สคริปต์ lint-all-shells.sh
#!/bin/sh
set -eu
# ตรวจสอบว่ามีคำสั่ง shellcheck ติดตั้งอยู่ในระบบหรือไม่
if ! command -v shellcheck >/dev/null 2>&1; then
printf "Error: 'shellcheck' command not found. Please install it first.\n" >&2
exit 1
fi
target_dir="${1:-.}"
failed_count=0
checked_count=0
printf "Scanning directory: %s\n\n" "$target_dir"
# ใช้ find ค้นหาไฟล์ปกติ โดยข้ามโฟลเดอร์ที่ไม่เกี่ยวข้อง เช่น .git, vendor, node_modules
# จัดการผ่าน While-loop เพื่อความเข้ากันได้กับ POSIX
find "$target_dir" \
-type d \( -name ".git" -o -name "vendor" -o -name "node_modules" -o -name ".build" \) -prune \
-o -type f -print | while IFS= read -r file; do
# ข้ามไฟล์เปล่า (ขนาด 0 ไบต์) หรือไฟล์ที่ไม่มีสิทธิ์อ่าน
[ -s "$file" ] && [ -r "$file" ] || continue
is_shell_script=0
# 1. ตรวจสอบจากนามสกุลไฟล์
case "$file" in
*.sh|*.bash|*.ksh)
is_shell_script=1
;;
*)
# 2. กรณีไม่มีนามสกุล: อ่านเฉพาะบรรทัดแรกสุดเพื่อตรวจจับ Shebang
first_line=""
IFS= read -r first_line < "$file" || [ -n "$first_line" ]
case "$first_line" in
'#!'*sh*)
# กรองเฉพาะ Shebang ที่มีคำว่า sh, bash, zsh, ksh, ash, dash
case "$first_line" in
*'/bin/'*sh*|*'/env '*sh*|*'/env '*bash*|*'/env '*zsh*)
is_shell_script=1
;;
esac
;;
esac
;;
esac
# ถ้ายืนยันว่าเป็น Shell Script ให้ส่งเข้า ShellCheck
if [ "$is_shell_script" -eq 1 ]; then
printf "==> Linting: %s\n" "$file"
if ! shellcheck "$file"; then
# แจ้งเตือนเมื่อพบข้อผิดพลาด
printf "FAILED: %s\n\n" "$file" >&2
exit 1
fi
printf "PASSED\n\n"
fi
done
printf "Linting completed successfully.\n"
วิธีนำไปใช้งาน
1. ให้สิทธิ์ Execute แก่ไฟล์
chmod +x lint-all-shells.sh
2. รันสแกนทั้งโปรเจกต์
# ตรวจสอบไดเรกทอรีปัจจุบัน
./lint-all-shells.sh
# หรือระบุพาธเป้าหมาย
./lint-all-shells.sh ./bin
จุดเด่นและการทำงานเชิงลึก
- ตรวจจับทั้งแบบมีและไม่มี Extension
รองรับทั้งdeploy.sh,script.bashและไฟล์ไบนารีสคริปต์ เช่นbin/my-toolที่มีบรรทัดแรกเป็น#!/usr/bin/env bashหรือ#!/bin/sh - ประหยัด I/O และหน่วยความจำ
ใช้คำสั่งread -r first_line < "$file"อ่านเฉพาะบรรทัดแรกสุดของแต่ละไฟล์ ไม่โหลดไฟล์ขนาดใหญ่ทั้งหมดลงใน Memory - POSIX Compliant
ใช้ไวยากรณ์case ... inในการ Match ข้อความและ Wildcard แทนการเรียกคำสั่งภายนอกอย่างgrepหรือ Bash Regex (=~) ทำให้ทำงานได้เร็วและรันได้บนทุกระบบ - ข้ามโฟลเดอร์ที่ไม่จำเป็น
ตัดพาธ.git,node_modules,vendorออกตั้งแต่ระดับfind -pruneเพื่อลดเวลาสแกนในโปรเจกต์ขนาดใหญ่