← สารบัญบทเรียน M13 · Shell Scripting เบื้องต้น
M13 · หัวข้อสอบ

Shell Scripting เบื้องต้น

จนถึงตอนนี้คุณพิมพ์คำสั่งทีละบรรทัด — บทนี้จะสอนวิธี “รวมหลายคำสั่งไว้ในไฟล์เดียว” แล้วสั่งทำงานทีเดียวจบ ไม่ต้องพิมพ์ซ้ำ ไม่ต้องจำ ทำซ้ำได้ทุกวัน

🌱 บทนี้เขียนเพื่อคนที่ไม่เคยเขียนโปรแกรมมาก่อนเลย

1script คืออะไร ทำไมต้องมี

สมมติทุกเช้าคุณต้องทำงานเดิมๆ 5 คำสั่ง: เช็กพื้นที่ดิสก์ → สำรองไฟล์ตั้งค่า → สร้าง user ใหม่ → เคลียร์ log → รีสตาร์ตบริการ ถ้าพิมพ์ทีละบรรทัดทุกวันก็เสียเวลาและพลาดง่าย

shell script คือการเอาคำสั่งทั้งหมดนั้น เขียนเรียงไว้ในไฟล์เดียว แล้วสั่งให้ทำงานทีเดียว เครื่องจะไล่ทำทุกบรรทัดตามลำดับให้เอง

💡 เปรียบเทียบให้เห็นภาพ

script ก็เหมือน สูตรอาหารที่จดไว้ — แทนที่จะต้องนึกเองทุกครั้งว่า “ตอกไข่ → ตีให้เข้ากัน → ตั้งกระทะ → เทลงไป” คุณเขียนขั้นตอนไว้ครั้งเดียว แล้วใครหยิบสูตรไปทำตามก็ได้ผลเหมือนกันทุกครั้ง ไม่ต้องจำ ไม่ตกหล่นขั้นตอน

เครื่องก็คือ “พ่อครัว” ที่อ่านสูตร (script) แล้วทำตามทีละบรรทัดจากบนลงล่าง

📌 ในข้อสอบ RHCSA ต้องเขียน script เป็น

ข้อสอบมักให้เขียน script สั้นๆ ที่ รับ argument และ มี if เช็กเงื่อนไข เช่น “เขียน script ที่รับชื่อมา 1 ตัว ถ้าเป็น start ให้ทำอย่างหนึ่ง ถ้าเป็น stop ให้ทำอีกอย่าง” — ไม่ต้องเก่งเขียนโปรแกรม แค่ครบ 5 อย่างในบทนี้ก็ทำข้อสอบได้

2บรรทัดแรกศักดิ์สิทธิ์: shebang

ทุก script ต้องเริ่มด้วยบรรทัดพิเศษที่เรียกว่า shebang หน้าตาแบบนี้:

📄 บรรทัดแรกของทุก script
#!/bin/bash

เครื่องหมาย #! (อ่านว่า “แช-แบง”) ตามด้วย path ของโปรแกรมที่จะใช้รันไฟล์นี้ — ในที่นี้คือ /bin/bash แปลว่า “ขอให้ใช้ bash อ่านและรันไฟล์นี้นะ”

💡 เปรียบเทียบ

shebang เหมือน ป้ายบอกภาษาบนหน้าปกหนังสือ — บอกล่วงหน้าว่า “เนื้อในเล่มนี้เป็นภาษา bash นะ” เครื่องจะได้เรียกล่ามที่ถูกตัว (bash) มาแปลให้

เซฟไฟล์เป็นนามสกุล .sh เช่น backup.sh เพื่อให้คนอื่นรู้ว่าเป็น script (จริงๆ Linux ไม่บังคับนามสกุล แต่ทำไว้ให้อ่านง่าย)

⚠️ shebang ต้องเป็นบรรทัดแรกสุดเท่านั้น

ถ้ามีบรรทัดว่างหรือมีช่องว่างนำหน้า #! จะใช้งานไม่ได้ ต้องเป็น อักขระแรกสุดของไฟล์ เป๊ะๆ

3ทำให้รันได้: chmod +x และ ./

เขียนไฟล์เสร็จยังรันไม่ได้ทันที เพราะไฟล์ใหม่ ยังไม่มีสิทธิ์ “รันได้” (execute) — จำเรื่องสิทธิ์ r w x จาก M5 ได้ไหม? เราต้องเปิดสิทธิ์ x ให้ก่อน

⌨️ จากเขียนเสร็จ → รันได้
[student@server1 ~]$ chmod +x backup.sh
[student@server1 ~]$ ./backup.sh
เริ่มสำรองข้อมูล...
เสร็จเรียบร้อย!
chmod +x backup.shเพิ่มสิทธิ์ “รันได้” (x) ให้กับไฟล์ — ทำครั้งเดียวพอ
./backup.shสั่งรัน script ที่อยู่ในโฟลเดอร์ปัจจุบัน

ทำไมต้องมี ./ ข้างหน้า?

คุณอาจสงสัยว่าทำไมพิมพ์ backup.sh เฉยๆ ไม่ได้ ต้องมี ./ นำหน้า คำตอบอยู่ที่ . = “โฟลเดอร์ที่ฉันอยู่ตอนนี้” (จาก M1)

💡 เปรียบเทียบ

เวลาคุณพิมพ์ ls เฉยๆ เครื่องจะไปค้นหาคำสั่งจาก “รายชื่อร้านค้าที่รู้จัก” (ตัวแปรชื่อ PATH — รวมที่อยู่ของคำสั่งระบบ) แต่ script ของคุณ ไม่ได้อยู่ในรายชื่อนั้น เครื่องเลยหาไม่เจอ

การใส่ ./ ก็เหมือนชี้นิ้วบอกตรงๆ ว่า “ไฟล์อยู่ตรงนี้ ในห้องที่ฉันยืนอยู่นี่แหละ” เครื่องจะได้ไม่ต้องไปหาที่อื่น

🎯 จำสองขั้นนี้ให้ขึ้นใจ

เขียน script เสร็จทุกครั้งให้ทำ 2 อย่าง: (1) chmod +x ชื่อไฟล์ เปิดสิทธิ์รัน แล้ว (2) ./ชื่อไฟล์ เพื่อสั่งรัน — ลืมข้อใดข้อหนึ่ง script ก็ไม่ทำงาน

4comment กับ echo

# — comment (โน้ตที่เครื่องไม่อ่าน)

ข้อความที่ขึ้นต้นด้วย # เครื่องจะ ข้ามไปเลย ไม่ทำงาน — มีไว้ให้ “คน” อ่านเข้าใจว่าบรรทัดถัดไปทำอะไร (ยกเว้น #! บรรทัดแรกที่เป็น shebang พิเศษ)

echo — แสดงข้อความออกหน้าจอ

คำสั่ง echo ก็คือ “พูดออกมา” — พิมพ์ข้อความที่เราใส่ออกมาบนจอ ใช้บอกความคืบหน้าให้ผู้ใช้รู้ว่า script ทำถึงไหนแล้ว

📄 hello.sh
#!/bin/bash
# script แรกของฉัน — ทักทายโลก
echo "สวัสดีชาว Linux!"
echo "นี่คือ script แรกของฉัน"

เมื่อรัน ./hello.sh จะได้ผลว่า:

สวัสดีชาว Linux!
นี่คือ script แรกของฉัน

5ตัวแปร (variable)

ตัวแปร คือ “กล่องที่ตั้งชื่อไว้” สำหรับเก็บค่าเอาไว้ใช้ซ้ำ — เก็บครั้งเดียว เรียกใช้ได้หลายที่ ถ้าจะแก้ค่าก็แก้ที่เดียว

📄 ตั้งค่าและเรียกใช้ตัวแปร
#!/bin/bash
name="สมชาย"
echo "สวัสดีคุณ $name"
echo "ยินดีต้อนรับคุณ ${name} อีกครั้ง"
name="สมชาย"ตั้งค่า เก็บคำว่า “สมชาย” ไว้ในกล่องชื่อ name
$nameเรียกใช้ ค่าในกล่อง — ใส่ $ นำหน้าชื่อ
${name}แบบมีปีกกา ความหมายเหมือนกัน แต่ชัดเจนกว่าเมื่อต้องเขียนติดกับตัวอักษรอื่น เช่น ${name}_backup
💡 เปรียบเทียบ

ตัวแปรเหมือน กล่องที่แปะป้ายชื่อ ในตู้เก็บของ — ตอนตั้งค่า (name="สมชาย") คือเอาของใส่กล่อง ตอนเรียกใช้ ($name) คือเปิดกล่องเอาของออกมาดู เครื่องหมาย $ ก็คือคำสั่ง “เปิดกล่องนี้ให้ดูหน่อย”

🎯 ห้ามเว้นวรรครอบเครื่องหมาย = เด็ดขาด!

นี่คือ error อันดับหนึ่งของมือใหม่ ต้องเขียนติดกันเป๊ะ:

✅ ถูก: name="สมชาย"
❌ ผิด: name = "สมชาย" (มีเว้นวรรค → bash จะนึกว่า name เป็นคำสั่ง แล้ว error)

6รับค่าจากภายนอก: argument

นอกจากตั้งค่าตัวแปรเองในไฟล์ เรายัง ส่งค่าเข้าไปตอนสั่งรัน ได้ด้วย ค่าพวกนี้เรียกว่า argument — script จะหยิบมาใช้ผ่านตัวแปรพิเศษ $1 $2 $3 ...

📄 greet.sh
#!/bin/bash
echo "ชื่อ script: $0"
echo "argument ตัวแรก: $1"
echo "argument ตัวที่สอง: $2"
echo "ส่งมาทั้งหมด $# ตัว: $@"

รันแบบนี้ (เว้นวรรคคั่นแต่ละค่า):

[student@server1 ~]$ ./greet.sh สมชาย สมหญิง
ชื่อ script: ./greet.sh
argument ตัวแรก: สมชาย
argument ตัวที่สอง: สมหญิง
ส่งมาทั้งหมด 2 ตัว: สมชาย สมหญิง
ตัวแปรพิเศษหมายถึง
$0ชื่อของ script เอง (เช่น ./greet.sh)
$1 $2 $3argument ตัวที่ 1, 2, 3 ตามลำดับ
$#จำนวน argument ที่ส่งเข้ามาทั้งหมด
$@argument ทุกตัวรวมกัน
💡 เปรียบเทียบ

argument เหมือน วัตถุดิบที่ยื่นให้พ่อครัวตอนสั่งอาหาร — สูตร (script) เขียนไว้แล้วว่า “เอาของชิ้นที่ 1 มาทอด” แต่ของชิ้นที่ 1 จะเป็นไก่หรือหมูก็แล้วแต่คุณยื่นให้ตอนนั้น $1 ก็คือ “ของชิ้นที่ 1 ที่เพิ่งยื่นให้”

7ถามผู้ใช้: read

อีกวิธีรับค่าคือ ถามตอนรันแบบโต้ตอบ ด้วยคำสั่ง read — script จะหยุดรอให้พิมพ์ แล้วเก็บสิ่งที่พิมพ์ลงตัวแปร

📄 ask.sh
#!/bin/bash
read -p "พิมพ์ชื่อของคุณ: " name
echo "สวัสดีคุณ $name !"
[student@server1 ~]$ ./ask.sh
พิมพ์ชื่อของคุณ: สมชาย
สวัสดีคุณ สมชาย !
read -p "..."แสดงข้อความถาม (prompt) แล้วรอรับคำตอบ
nameชื่อตัวแปรที่จะเก็บคำตอบ (ไม่ต้องใส่ $ ตอนรับค่า ใส่ตอนเรียกใช้)

8เก็บผลคำสั่งลงตัวแปร

บางครั้งเราอยากเอา ผลลัพธ์ของคำสั่ง มาเก็บไว้ในตัวแปร เช่นเก็บวันที่วันนี้ ทำได้ด้วย $( คำสั่ง ) เรียกเทคนิคนี้ว่า command substitution

📄 เก็บวันที่ลงตัวแปร
#!/bin/bash
today=$(date +%F)
echo "วันนี้คือ $today"
echo "กำลังสร้างไฟล์ backup-$today.tar.gz"
วันนี้คือ 2026-06-16
กำลังสร้างไฟล์ backup-2026-06-16.tar.gz
$(date +%F)รันคำสั่ง date +%F (ได้วันที่รูปแบบ ปี-เดือน-วัน) แล้วเอาผลลัพธ์ยัดลงตัวแปร today
💡 เปรียบเทียบ

$(...) เหมือน ใช้คนอื่นไปถามมาก่อน — แทนที่คุณจะเขียนวันที่ลงไปเองตายตัว คุณส่งคนไปถามนาฬิกาว่า “วันนี้ที่เท่าไหร่” แล้วเอาคำตอบกลับมาเก็บ ทุกครั้งที่รันก็จะได้วันที่ปัจจุบันเสมอ

9ตัดสินใจ: if / else

หัวใจของ script ที่ฉลาดขึ้นคือการ ตัดสินใจ — “ถ้าเงื่อนไขเป็นจริง ทำอย่างนี้ ไม่งั้นทำอีกอย่าง” ใช้โครงสร้าง if

📄 โครงสร้าง if เต็มรูปแบบ
if [ เงื่อนไข ]; then
    # ทำเมื่อเงื่อนไขจริง
elif [ เงื่อนไขอื่น ]; then
    # ทำเมื่อเงื่อนไขที่สองจริง
else
    # ทำเมื่อไม่เข้าเงื่อนไขไหนเลย
fi
if ... thenเริ่มเช็กเงื่อนไข
elif“ไม่งั้นถ้า...” เช็กเงื่อนไขเพิ่ม (มีหรือไม่มีก็ได้)
else“ไม่งั้น...” ทำเมื่อไม่เข้าเงื่อนไขข้างบนเลย (มีหรือไม่มีก็ได้)
fiปิดบล็อก if (คือ if สะกดกลับหลัง) — ลืมไม่ได้!
🎯 ต้องมีเว้นวรรคในวงเล็บ [ ] ทุกด้าน

วงเล็บเหลี่ยม [ ] จริงๆ คือคำสั่งทดสอบ ต้องมีช่องว่างคั่น:

✅ ถูก: if [ "$x" = "yes" ]; then
❌ ผิด: if ["$x"="yes"]; then (ไม่เว้นวรรค → error)

ตัวเทียบที่ใช้บ่อย

การเขียนเงื่อนไขต่างกันตามชนิดของสิ่งที่เทียบ:

ใช้กับตัวเทียบความหมาย
ตัวเลข-eqเท่ากัน (equal)
-neไม่เท่ากัน (not equal)
-ltน้อยกว่า (less than)
-gtมากกว่า (greater than)
-leน้อยกว่าหรือเท่ากับ
-geมากกว่าหรือเท่ากับ
สตริง (ข้อความ)=ข้อความเท่ากัน
!=ข้อความไม่เท่ากัน
ไฟล์-f ไฟล์มีไฟล์นี้อยู่จริงไหม
-d โฟลเดอร์มีโฟลเดอร์นี้อยู่จริงไหม
-z สตริงสตริงว่างเปล่าหรือเปล่า
📄 check-age.sh — เทียบตัวเลข
#!/bin/bash
age=$1
if [ "$age" -ge 18 ]; then
    echo "คุณบรรลุนิติภาวะแล้ว"
else
    echo "คุณยังเป็นเยาวชน"
fi

รัน ./check-age.sh 20 → ได้ “คุณบรรลุนิติภาวะแล้ว” เพราะ 20 มากกว่าหรือเท่ากับ 18

📄 check-file.sh — เช็กว่ามีไฟล์ไหม
#!/bin/bash
if [ -f /etc/passwd ]; then
    echo "เจอไฟล์ /etc/passwd"
else
    echo "ไม่เจอไฟล์"
fi

10วนซ้ำ: for และ while

เวลาต้องทำงานเดิมกับหลายๆ ค่า เราไม่ต้องเขียนซ้ำ — ใช้ loop วนทำให้

for — วนตามรายการ

เหมาะเวลามี “รายการ” ชัดเจนว่าจะวนกี่รอบ

📄 for แบบพื้นฐาน
#!/bin/bash
for i in 1 2 3; do
    echo "รอบที่ $i"
done
รอบที่ 1
รอบที่ 2
รอบที่ 3
for i in ...วนโดยให้ i เปลี่ยนค่าไปทีละตัวในรายการ
do ... doneคลุมคำสั่งที่จะทำซ้ำ — done ปิดบล็อก (ลืมไม่ได้!)
📄 for วนไฟล์ทุกไฟล์ .txt
#!/bin/bash
for f in *.txt; do
    echo "กำลังประมวลผลไฟล์: $f"
done

*.txt จะถูกแทนที่ด้วยรายชื่อไฟล์ .txt ทุกไฟล์ในโฟลเดอร์ปัจจุบัน แล้ววนทำทีละไฟล์

📄 ตัวอย่างจริง: สร้าง user หลายคนรวดเดียว
#!/bin/bash
for user in alice bob charlie; do
    useradd $user
    echo "สร้าง user $user เรียบร้อย"
done

แทนที่จะพิมพ์ useradd สามรอบ ก็เขียนชื่อทั้งหมดในรายการ แล้วให้ loop วนสร้างให้ทีละคน — ถ้ามี 50 คนก็แค่เพิ่มชื่อเข้าไป

while — วนตราบเท่าที่เงื่อนไขยังจริง

เหมาะเวลายังไม่รู้แน่ว่าจะวนกี่รอบ แต่รู้ว่า “วนไปเรื่อยๆ จนกว่าเงื่อนไขจะเลิกเป็นจริง”

📄 while นับ 1 ถึง 3
#!/bin/bash
count=1
while [ "$count" -le 3 ]; do
    echo "นับเลข $count"
    count=$((count + 1))
done
นับเลข 1
นับเลข 2
นับเลข 3
while [ ... ]วนต่อไปตราบที่เงื่อนไขยังจริง (ในที่นี้คือ count ยัง ≤ 3)
$((count + 1))คำนวณเลข — เพิ่มค่า count ทีละ 1 ในแต่ละรอบ
⚠️ ระวัง loop ไม่รู้จบ (infinite loop)

ใน while ถ้าลืมเพิ่มค่าตัวนับ (เช่นลืมบรรทัด count=$((count + 1))) เงื่อนไขจะจริงตลอด → วนไม่หยุด เครื่องค้าง! ถ้าเจอแบบนี้กด Ctrl + C เพื่อหยุด

11exit code: สำเร็จหรือพัง?

ทุกคำสั่งที่รันเสร็จจะ คืนรหัสบอกผล เรียกว่า exit code เก็บอยู่ในตัวแปรพิเศษ $? — เช็กได้ว่าคำสั่งที่เพิ่งรันสำเร็จหรือไม่

ค่า $?หมายถึง
0สำเร็จ — ทุกอย่างเรียบร้อย
ไม่ใช่ 0 (เช่น 1, 2, 127)ผิดพลาด — มีบางอย่างไม่สำเร็จ
⌨️ เช็ก $? หลังรันคำสั่ง
[student@server1 ~]$ ls /etc/passwd
/etc/passwd
[student@server1 ~]$ echo $?
0
[student@server1 ~]$ ls /ไม่มีไฟล์นี้
ls: cannot access '/ไม่มีไฟล์นี้': No such file or directory
[student@server1 ~]$ echo $?
2

คำสั่งแรกสำเร็จ → $? เป็น 0 ส่วนคำสั่งที่หาไฟล์ไม่เจอ → $? ไม่ใช่ 0

💡 เปรียบเทียบ

exit code เหมือน คำตอบสั้นๆ ของลูกน้องหลังทำงานเสร็จ — ตอบ “เรียบร้อยครับ” (0) หรือ “มีปัญหาครับ” (ไม่ใช่ 0) จำง่ายๆ ว่า 0 = ดี (ผิดจากชีวิตประจำวันที่ 0 มักแปลว่าแย่)

exit — จบ script พร้อมบอกผล

ใน script เราใช้ exit 0 เพื่อจบงานแบบ “สำเร็จ” หรือ exit 1 เพื่อจบแบบ “มีปัญหา” ให้คนหรือ script อื่นที่เรียกเราเอาไปเช็กต่อได้

📄 ใช้ exit บอกผล
#!/bin/bash
if [ -f /etc/passwd ]; then
    echo "เจอไฟล์ — สำเร็จ"
    exit 0
else
    echo "ไม่เจอไฟล์ — ล้มเหลว"
    exit 1
fi

12ตัวอย่าง script สมบูรณ์

รวมทุกอย่างในบทนี้: รับชื่อ user เข้ามา 1 ตัว แล้วเช็กว่ามี user นั้นอยู่ในระบบหรือไม่

📄 check-user.sh
#!/bin/bash
# เช็กว่ามี user ตามชื่อที่ส่งมาในระบบหรือไม่

# ขั้นที่ 1: เช็กว่าส่งชื่อมาหรือเปล่า
if [ "$#" -ne 1 ]; then
    echo "วิธีใช้: $0 <ชื่อ-user>"
    exit 1
fi

username=$1

# ขั้นที่ 2: ค้นชื่อในไฟล์ /etc/passwd
if grep -q "^$username:" /etc/passwd; then
    echo "✅ มี user '$username' อยู่ในระบบ"
    exit 0
else
    echo "❌ ไม่พบ user '$username'"
    exit 1
fi

ลองรันสองแบบ:

[student@server1 ~]$ chmod +x check-user.sh
[student@server1 ~]$ ./check-user.sh root
✅ มี user 'root' อยู่ในระบบ
[student@server1 ~]$ ./check-user.sh ผีไม่มีจริง
❌ ไม่พบ user 'ผีไม่มีจริง'
"$#" -ne 1ถ้าจำนวน argument ไม่เท่ากับ 1 แสดงวิธีใช้แล้วออกด้วย exit 1
grep -q "^$username:"ค้นบรรทัดที่ขึ้นต้นด้วยชื่อ user ในไฟล์ /etc/passwd (-q = เงียบ ไม่ต้องแสดงผล แค่บอกเจอ/ไม่เจอผ่าน exit code)
💡 สังเกตว่าใช้ครบทุกชิ้นส่วน

script เดียวนี้รวม: shebang, comment, ตัวแปร, argument ($1 $#), if/else, การค้นด้วย grep, และ exit code — นี่คือทรงที่ข้อสอบ RHCSA ชอบออก

13ข้อผิดพลาดที่เจอบ่อย

14สรุปบทนี้ (Cheat Sheet)

สิ่งที่ต้องรู้เขียนอย่างไร
บรรทัดแรก (shebang)#!/bin/bash
comment (เครื่องข้าม)# ข้อความ
แสดงข้อความecho "ข้อความ"
ตั้งตัวแปรname="ค่า" (ห้ามเว้นวรรครอบ =)
เรียกใช้ตัวแปร$name หรือ ${name}
argument / ชื่อ script$1 $2 / $0
จำนวน / ทุก argument$# / $@
ถามผู้ใช้read -p "ถาม: " ตัวแปร
เก็บผลคำสั่งtoday=$(date +%F)
เงื่อนไขif [ เงื่อนไข ]; then ... else ... fi
เทียบตัวเลข-eq -ne -lt -gt -le -ge
เทียบสตริง=   !=
เทียบไฟล์-f มีไฟล์   -d มีโฟลเดอร์   -z สตริงว่าง
วนตามรายการfor i in 1 2 3; do ... done
วนตามเงื่อนไขwhile [ เงื่อนไข ]; do ... done
ผลคำสั่งล่าสุด$? (0 = สำเร็จ)
จบ scriptexit 0 สำเร็จ / exit 1 ผิดพลาด
ทำให้รันได้ + รันchmod +x ไฟล์.sh แล้ว ./ไฟล์.sh
✅ จบบทนี้คุณควรทำได้

เขียนไฟล์ .sh ที่มี shebang → ใช้ตัวแปรและรับ argument เป็น → เขียน if เช็กเงื่อนไขและ for/while วนซ้ำได้ → chmod +x แล้วรันด้วย ./ เท่านี้คุณก็เขียน script อัตโนมัติงานซ้ำๆ และทำข้อ scripting ในห้องสอบได้แล้ว!