Shell Script: ShellCheck

ShellCheck คือเครื่องมือ Static Analysis (Linter) แบบโอเพนซอร์สสำหรับ Shell Script พัฒนาด้วยภาษา Haskell โดย Vidar Holen จุดประสงค์หลักคือช่วยค้นหาข้อผิดพลาดทางไวยากรณ์ (Syntax Errors), จุดเสี่ยงด้านความปลอดภัย (Security Vulnerabilities), ปัญหาการพกพาข้ามระบบ (Portability / Bashisms), และพฤติกรรมที่ไม่คาดคิดของสคริปต์ก่อนนำไปรันจริงบน Production


ทำไม Shell Script จึงต้องการ Linter อย่าง ShellCheck?

Shell Script (ไม่ว่าจะเป็น sh, bash, หรือ dash) เป็นภาษาที่ยืดหยุ่นสูงและไม่มีกระบวนการ Compilation ทำให้ข้อผิดพลาดหลายอย่างไม่แสดงตัวออกมาจนกว่าเงื่อนไขใน Runtime จะทำงานจริง

  • ไม่มี Type Safety และจัดการช่องว่าง (Whitespace) ซับซ้อน
    ข้อผิดพลาดเล็กน้อย เช่น การลืมครอบเครื่องหมายคำพูดรอบตัวแปร ($file vs "$file") อาจทำให้คำสั่งธรรมดากลายเป็นช่องโหว่ร้ายแรง
  • พฤติกรรมที่แตกต่างกันตามระบบ
    สคริปต์ที่รันผ่านบนเครื่องพัฒนาที่เป็น macOS หรือ Ubuntu อาจพังทันทีเมื่อนำไปรันบน Alpine Linux ใน Docker เพราะเผลอใช้ฟีเจอร์เฉพาะของ Bash
  • ดักจับคำสั่งอันตราย
    ป้องกันอุบัติเหตุร้ายแรง เช่น การเขียนคำสั่งลบไฟล์ที่ขยายค่าผิดพลาดกลายเป็น rm -rf /

ปัญหาคลาสสิกที่ ShellCheck ช่วยตรวจจับ


1. การไม่ครอบ Double Quotes รอบตัวแปร (Quoting issues)

# โค้ดที่ผิด:
rm -rf $target_dir/*

# ShellCheck แจ้งเตือน: SC2086: Double quote to prevent globbing and word splitting.
# หาก $target_dir ว่างเปล่า คำสั่งจะกลายเป็น `rm -rf /*` ทันที

# โค้ดที่ถูกต้อง:
rm -rf "${target_dir:?}"/*


2. การเขียนเงื่อนไขเปรียบเทียบผิดพลาด (Incorrect Comparison)

# โค้ดที่ผิด:
if [ $count = 0 ]; then ...

# ShellCheck แจ้งเตือน: SC2070: -eq should be used for numeric comparisons.

# โค้ดที่ถูกต้อง:
if [ "$count" -eq 0 ]; then ...


3. การตรวจจับคำสั่งเฉพาะระบบ (Bashisms Detection)

หากไฟล์สคริปต์ระบุ Shebang เป็น #!/bin/sh แต่เผลอใช้ฟังก์ชันของ Bash

#!/bin/sh
if [[ $name == "Pitt" ]]; then
    echo "Hello"
fi

# ShellCheck แจ้งเตือน:
# SC3010: In POSIX sh, [[ ]] is not supported.
# SC3014: In POSIX sh, == in place of = is not supported.


ระดับความรุนแรงของการแจ้งเตือน (Severity Levels)

ShellCheck แบ่งระดับการตรวจจับออกเป็น 4 ระดับ

ระดับ (Severity)คำอธิบายตัวอย่าง
Errorไวยากรณ์ผิดพลาดอย่างร้ายแรง สคริปต์จะทำงานล้มเหลวแน่นอนลืมปิด quote, วงเล็บไม่ตรงคู่, ปีกกาผิด
Warningสคริปต์อาจรันผ่าน แต่พฤติกรรมมีโอกาสผิดพลาดสูงใน Runtimeลืมครอบ quote, การเปรียบเทียบผิดประเภท
Infoแนะนำแนวทางปฏิบัติที่ดี หรือลดความกำกวมของคำสั่งการใช้ $() แทน backticks (`...`)
Styleข้อเสนอแนะเชิงรูปแบบและความกระชับของโค้ดตัวแปรไม่ได้ถูกเรียกใช้ (Unused variable)

วิธีการติดตั้งและการใช้งาน

1. ติดตั้งผ่าน Package Manager

  • macOS (Homebrew)
    brew install shellcheck
  • Debian / Ubuntu
    sudo apt-get install shellcheck
  • Alpine Linux
    apk add shellcheck

2. การรันตรวจสอบผ่าน CLI

  • ตรวจสอบสคริปต์แบบระบุเชลล์เป้าหมาย:Bash# บังคับตรวจตามมาตรฐาน POSIX shellcheck -s sh deploy.sh # ตรวจสอบตามมาตรฐาน Bash shellcheck -s bash setup.sh
  • กำหนดระดับความเข้มงวดในการแจ้งเตือน (เช่น แสดงเฉพาะ Warning ขึ้นไป):Bashshellcheck -S warning deploy.sh

การผสานรวมเข้ากับกระบวนการทำงาน (Integration)

1. ใช้งานใน VS Code

ติดตั้ง Extension ชื่อ ShellCheck ของ Timon Wong ซึ่งจะไฮไลต์เส้นใต้สีแดง/เหลืองในโค้ดแบบ Real-time พร้อมแสดงรหัสโค้ด (เช่น SC2086) และปุ่ม Quick Fix

2. การควบคุมพฤติกรรมเฉพาะจุด (Inline Directives)

หากมีบรรทัดที่ตั้งใจให้เกิด Word Splitting หรือจำเป็นต้องละเว้นการตรวจ สามารถใส่คอมเมนต์กำกับได้

# shellcheck disable=SC2086
echo $list_of_args

3. การนำเข้า CI/CD Pipeline (ตัวอย่าง GitHub Actions)

ช่วยให้มั่นใจว่าโค้ดทุกชุดที่ Merge เข้าสู่ Repository มีมาตรฐานความปลอดภัย

name: Lint Shell Scripts
on: [push, pull_request]

jobs:
  shellcheck:
    runs-edge: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run ShellCheck
        run: |
          shellcheck $(find . -type f -name "*.sh")


ShellCheck เปลี่ยนการเขียนสคริปต์จากการพึ่งพาการลองผิดลองถูก มาเป็นการพัฒนาระบบที่มีการทดสอบเชิงคุณภาพตั้งแต่ต้นทาง ช่วยลดความเสี่ยงต่อเซิร์ฟเวอร์ และประหยัดเวลาในการ Debug ปัญหาที่หาสาเหตุได้ยากในภายหลัง