เชลล์ (Shell) เป็นตัวแปลคำสั่ง (Command Interpreter) สำหรับระบบปฏิบัติการ Bash เป็นเชลล์ที่ผมชอบมากที่สุด แต่งทุกเชลล์ Linux จะแปลคำสั่งที่ผู้ใช้หรือซิสแอดมินพิมพ์เข้าไป ให้อยู่ในรูปแบบที่ระบบปฏิบัติการใช้งานได้ เมื่อผลลัพธ์ถูกส่งกลับไปยังโปรแกรมเชลล์ มันจะส่งไปยัง STDOUT ซึ่งโดยค่าเริ่มต้นจะแสดงผลในเทอร์มินัล ทุกเชลล์ที่ผมรู้จัก ยังเป็นภาษาโปรแกรมมิ่งอีกด้วย
คุณสมบัติอย่าง Tab Completion, การเรียกคำสั่งย้อนหลังและแก้ไข (Command-line Recall and Editing) และทางลัดอย่าง Aliases ทั้งหมดช่วยเพิ่มคุณค่าให้เป็นเชลล์ที่ทรงพลัง โหมดการแก้ไขบรรทัดคำสั่งเริ่มต้นของมันใช้ Emacs แต่คุณสมบัติโปรดของผมใน Bash คือสามารถเปลี่ยนเป็นโหมด Vi ได้ เพื่อใช้คำสั่งแก้ไขที่อยู่ใน Muscle Memory ของผมอยู่แล้ว
อย่างไรก็ตาม หากคุณคิดว่ Bash เป็นเพียงแค่เชลล์ คุณจะพลาดพลังจริงของมันไป ขณะค้นคว้าสำหรับหลักสูตร Linux เรียนรู้ด้วยตนเอง 3 เล่มของผม (ซึ่งชุดบทความนี้อิงจาก) ผมได้เรียนรู้สิ่งต่างๆ เกี่ยวกับ Bash ที่ไม่เคยรู้มาก่อนใน 20 ปีกว่าที่ทำงานกับ Linux บางส่วนของความรู้ใหม่นี้เกี่ยวข้องกับการใช้เป็นภาษาโปรแกรมมิ่ง Bash เป็นภาษาโปรแกรมมิ่งที่ทรงพลัง ออกแบบมาอย่างสมบูรณ์สำหรับการใช้งานบนบรรทัดคำสั่งและในเชลล์สคริปต์
ชุดบทความ 3 ส่วนนี้ สำรวจการใช้ Bash ในฐานะภาษาโปรแกรมบนอินเทอร์เฟซบรรทัดคำสั่ง (CLI) บทความแรกนี้ สำรวจการเขียนโปรแกรมบรรทัดคำสั่งพื้นฐานด้วย Bash ตัวแปร และตัวดำเนินการควบคุม บทความอื่นๆ จะสำรวจประเภทของไฟล์ Bash; ตัวดำเนินการตรรกะสตริง ตัวเลข และทั่วไป ที่ให้การควบคุมโฟลว์การทำงาน; ประเภทต่างๆ ของการขยายเชลล์; และลูป for, while, และ until ที่ทำให้สามารถทำงานซ้ำได้ พร้อมทั้งดูคำสั่งบางอย่างที่ช่วยลดความซับซ้อนและรองรับการใช้เครื่องมือเหล่านี้
เชลล์ (The Shell)
เชลล์เป็นตัวแปลคำสั่งสำหรับระบบปฏิบัติการ Bash เป็นเชลล์ที่ผมชอบ แต่งทุกเชลล์ Linux แปลคำสั่งที่ผู้ใช้หรือซิสแอดมินพิมพ์ ให้อยู่ในรูปแบบที่ระบบปฏิบัติการใช้งานได้ เมื่อผลลัพธ์กลับมายังโปรแกรมเชลล์ มันจะแสดงในเทอร์มินัล ทุกเชลล์ที่ผมรู้จัก เป็นภาษาโปรแกรมมิ่งด้วย
Bash ย่อมาจาก Bourne Again Shell เพราะ Bash shell พัฒนาจาก Bourne shell เก่า ที่ Steven Bourne เขียนในปี 1977 มีเชลล์อื่นๆ หลายตัว แต่ 4 ตัวนี้เป็นที่ผมเจอบ่อยที่สุด:
- csh: C Shell สำหรับโปรแกรมเมอร์ที่ชอบไวยากรณ์ภาษา C
- ksh: Korn Shell เขียนโดย David Korn นิยมในชุมชน Unix
- tcsh: รุ่นของ csh ที่มีคุณสมบัติใช้งานง่ายกว่า
- zsh: Z Shell รวมคุณสมบัติของเชลล์ยอดนิยมหลายตัว
ทุกเชลล์มีคำสั่งในตัว (Built-in Commands) ที่เสริมหรือแทนที่คำสั่งจาก Core Utilities เปิด man page ของเชลล์และหาส่วน "BUILT-INS" เพื่อดูคำสั่งที่มันให้มา
แต่ละเชลล์มีบุคลิกภาพและไวยากรณ์เฉพาะ บางตัวจะเหมาะกับคุณมากกว่า ผมเคยใช้ C Shell, Korn Shell, และ Z Shell แต่ยังชอบ Bash มากกว่า ใช้ตัวที่เหมาะกับคุณ แม้อาจต้องลองหลายตัว แต่โชคดีที่เปลี่ยนเชลล์ง่ายมาก
เชลล์ทั้งหมดนี้ เป็นภาษาโปรแกรมมิ่ง พร้อมเป็นตัวแปลคำสั่ง นี่คือการทัวร์เร็วๆ ของโครงสร้างและเครื่องมือโปรแกรมมิ่งบางอย่างที่เป็นส่วนประกอบสำคัญของ Bash
Bash ในฐานะภาษาโปรแกรมมิ่ง
ซิสแอดมินส่วนใหญ่เคยใช้ Bash ให้คำสั่งที่มักจะง่ายและตรงไปตรงมา แต่ Bash สามารถทำได้มากกว่าการพิมพ์คำสั่งเดี่ยว และซิสแอดมินหลายคนสร้างโปรแกรมบรรทัดคำสั่งง่ายๆ เพื่อทำชุดงานต่อเนื่อง โปรแกรมเหล่านี้เป็นเครื่องมือทั่วไปที่ช่วยประหยัดเวลาและความพยายาม
เป้าหมายของผมตอนเขียนโปรแกรม CLI คือการประหยัดเวลาและความพยายาม (คือเป็น "Lazy Sysadmin") โปรแกรม CLI รองรับสิ่งนี้โดยแสดงรายการคำสั่งหลายคำสั่งในลำดับเฉพาะ ที่รันต่อเนื่องกัน จึงไม่ต้องมองดูความคืบหน้าของคำสั่งหนึ่งแล้วพิมพ์คำสั่งถัดไปเมื่อเสร็จ คุณสามารถไปทำอย่างอื่นได้ โดยไม่ต้องติดตามความคืบหน้าของแต่ละคำสั่งตลอดเวลา
"โปรแกรม" (Program) คืออะไร?
พจนานุกรมคอมพิวติ้งออนไลน์ฟรี (FOLDOC) นิยามโปรแกรมว่า: "คำสั่งที่คอมพิวเตอร์รัน ต่างจากอุปกรณ์ทางกายภาพที่มันรันอยู่" WordNet ของ Princeton University นิยามว่า: "…ลำดับคำสั่งที่คอมพิวเตอร์สามารถแปลและรันได้…" Wikipedia ก็มีรายการดีๆ เกี่ยวกับโปรแกรมคอมพิวเตอร์
ดังนั้น โปรแกรมอาจประกอบด้วยคำสั่ง 1 หรือหลายคำสั่ง ที่ทำงานเฉพาะเจาะจงและเกี่ยวข้องกัน คำสั่งโปรแกรมคอมพิวเตอร์ เรียกอีกอย่างว่า "คำสั่งโปรแกรม" (Program Statement) สำหรับซิสแอดมิน โปรแกรมมักเป็นลำดับคำสั่งเชลล์ ทุกเชลล์ที่มีสำหรับ Linux ตัวที่ผมรู้จัก มีความสามารถโปรแกรมมิ่งขั้นพื้นฐานอย่างน้อย และ Bash ซึ่งเป็นเชลล์เริ่มต้นของส่วนใหญ่ของ Linux Distribution ก็ไม่ใช่ข้อยกเว้น
แม้ชุดนี้ใช้ Bash (เพราะมีอยู่ทุกที่) หากคุณใช้เชลล์อื่น แนวคิดโปรแกรมมิ่งทั่วไปจะเหมือนกัน แม้โครงสร้างและไวยากรณ์อาจต่างออกไป บางเชลล์อาจรองรับคุณสมบัติที่บางตัวไม่มี แต่ทุกตัวให้ความสามารถโปรแกรมมิ่งบางส่วน โปรแกรมเชลล์สามารถเก็บในไฟล์เพื่อใช้ซ้ำ หรือสร้างบนบรรทัดคำสั่งตามต้องการ
โปรแกรม CLI เรียบง่าย
โปรแกรมบรรทัดคำสั่งที่เรียบง่ายที่สุด คือ คำสั่งโปรแกรม 1 หรือ 2 คำสั่ง ที่ติดต่อกัน ป้อนบนบรรทัดคำสั่งก่อนกด Enter คำสั่งที่สองในโปรแกรม หากมี อาจขึ้นอยู่กับการกระทำของคำสั่งแรก แต่ไม่จำเป็น
ยังมีเครื่องหมายวรรคตอนไวยากรณ์หนึ่งที่ต้องระบุชัดเจน เมื่อป้อนคำสั่งเดี่ยวบนบรรทัดคำสั่ง การกด Enter จะจบคำสั่งด้วยเซมิโคลอนโดยนัย (Implicit Semicolon ;) เมื่อใช้ในโปรแกรมเชลล์ CLI ที่ป้อนเป็นบรรทัดเดียวบนบรรทัดคำสั่ง เซมิโคลอนต้องใช้จบคำสั่งแต่ละคำสั่งและแยกจากคำสั่งถัดไป คำสั่งสุดท้ายในโปรแกรมเชลล์ CLI สามารถใช้เซมิโคลอนโดยนัยหรือแสดงชัด (Explicit) ก็ได้
ไวยากรณ์พื้นฐานบางอย่าง
ตัวอย่างต่อไปจะชี้แจงไวยากรณ์นี้ โปรแกรมนี้ประกอบด้วยคำสั่งเดียวพร้อมตัวจบชัดเจน:
[student@studentvm1 ~]$ echo "Hello world." ;
Hello world.
อาจดูไม่ค่อยเป็นโปรแกรม แต่นี่คือโปรแกรมแรกที่ผมพบกับทุกภาษาโปรแกรมใหม่ที่เรียน ไวยากรณ์อาจต่างกันเล็กน้อย แต่ผลลัพธ์เหมือนกัน
มาขยายโปรแกรมเล็กๆ นี้ซึ่งพบได้ทุกที่ ผลลัพธ์ของคุณจะต่างจากผม เพราะผมทดลองทำอย่างอื่น ในขณะที่คุณอาจมีเฉพาะไดเรกทอรีและไฟล์เริ่มต้นที่สร้างขึ้นครั้งแรกที่ล็อกอินเข้าบัญชีผ่านเดสก์ท็อป GUI
[student@studentvm1 ~]$ echo "My home directory." ; ls ;
My home directory.
chapter25 TestFile1.Linux dmesg2.txt Downloads newfile.txt softlink1 testdir6
chapter26 TestFile1.mac dmesg3.txt file005 Pictures Templates testdir
TestFile1 Desktop dmesg.txt link3 Public testdir Videos
TestFile1.dos dmesg1.txt Documents Music random.txt testdir1
นี่เข้าใจง่ายขึ้น ผลลัพธ์เกี่ยวข้องกัน แตคำสั่งโปรแกรมแต่ละคำสั่งเป็นอิสระต่อกัน สังเกตว่าผมชอบเว้นวรรคก่อนและหลังเซมิโคลอน เพราะทำให้โค้ดอ่านง่ายขึ้น ลองโปรแกรม CLI เล็กนี้นี้อีกครั้งโดยไม่มีเซมิโคลอนชัดเจนท้ายสุด:
[student@studentvm1 ~]$ echo "My home directory." ; ls
ไม่มีความแตกต่างในเอาต์พุต
เรื่องของตัวแปร (Variables)
เหมือนภาษาโปรแกรมทั้งหมด เชลล์ Bash จัดการตัวแปรได้ ตัวแปรคือชื่อสัญลักษณ์ที่อ้างอิงตำแหน่งเฉพาะในหน่วยความจำที่มีค่าบางประการ ค่าของตัวแปรเปลี่ยนแปลงได้ คือ มันเปลี่ยนแปลงได้ (Variable)
Bash ไม่ได้กำหนดชนิดตัวแปร (Type Variables) เหมือน C และภาษาที่เกี่ยวข้อง กำหนดเป็นจำนวนเต็ม จุดทศนิยม หรือประเภทสตริง ใน Bash ทุกตัวแปรเป็นสตริง สตริงที่เป็นจำนวนเต็ม สามารถใช้ในคณิตศาสตร์จำนวนเต็ม ซึ่งเป็นประเภทคณิตศาสตร์เพียงประเภทเดียวที่ Bash ทำได้ หากต้องการคณิตศาสตร์ซับซ้อนกว่า สามารถใช้คำสั่ง bc ในโปรแกรม CLI และสคริปต์
ตัวแปรถูกกำหนดค่า และสามารถใช้อ้างอิงค่าเหล่านั้นในโปรแกรม CLI และสคริปต์ ค่าของตัวแปรตั้งค่าโดยใช้ชื่อ แต่ไม่ต้องนำหน้าด้วยเครื่องหมาย $ การกำหนด VAR=10 ตั้งค่าตัวแปร VAR เป็น 10 เพื่อพิมพ์ค่าตัวแปร ใช้คำสั่ง echo $VAR เริ่มต้นด้วยตัวแปรข้อความ (Non-numeric)
ตัวแปร Bash กลายเป็นส่วนหนึ่งของสภาพแวดล้อมเชลล์ จนกว่าจะถูกยกเลิก (Unset)
ตรวจสอบค่าเริ่มต้นของตัวแปรที่ยังไม่ได้กำหนดค่า ควรเป็น null จากนั้นกำหนดค่าให้ตัวแปรและพิมพ์เพื่อตรวจสอบ สามารถทำทั้งหมดนี้ในโปรแกรม CLI เดียว:
[student@studentvm1 ~]$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World
[student@studentvm1 ~]$
หมายเหตุ: ไวยากรณ์การกำหนดตัวแปรเข้มงวดมาก ห้ามมีช่องว่างทั้งสองข้างเครื่องหมายเท่ากับ (=) ในคำสั่งกำหนดค่า
บรรทัดว่างบ่งชี้ว่าค่าเริ่มต้นของ MyVar เป็น null การเปลี่ยนและตั้งค่าตัวแปรทำแบบเดียวกัน ตัวอย่างนี้แสดงทั้งค่าเดิมและค่าใหม่
như đã nói Bash สามารถคำนวณเลขคณิตจำนวนเต็มได้ ซึ่งมีประโยชน์สำหรับคำนวณอ้างอิงตำแหน่งองค์ประกอบในอาร์เรย์ หรือโจทย์คณิตศาสตร์ง่ายๆ ไม่เหมาะกับการคำนวณทางวิทยาศาสตร์ หรือสิ่งที่ต้องการทศนิยม เช่น การคำนวณการเงิน มีเครื่องมือดีกว่าสำหรับประเภทการคำนวณเหล่านั้น
นี่คือการคำนวณง่ายๆ:
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63
เกิดอะไรขึ้นเมื่อคำนวณคณิตศาสตร์ที่ได้ผลลัพธ์เป็นทศนิยม?
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1/Var2))"
Result = 0
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var2/Var1))"
Result = 1
[student@studentvm1 ~]$
ผลลัพธ์เป็นจำนวนเต็มที่ใกล้เคียงที่สุด สังเกตว่าการคำนวณทำเป็นส่วนหนึ่งของคำสั่ง echo คณิตศาสตร์ทำก่อนคำสั่ง echo ที่ห่อหุ้ม เนื่องจากลำดับความสำคัญ (Precedence) ของ Bash ดูรายละเอียดใน man page ของ Bash ค้นหา "precedence"
ตัวดำเนินการควบคุม (Control Operators)
ตัวดำเนินการควบคุมเชลล์ (Shell Control Operators) เป็นหนึ่งในตัวดำเนินการไวยากรณ์ สำหรับสร้างโปรแกรม CLI ที่น่าสนใจได้ง่าย รูปแบบง่ายที่สุดของโปรแกรม CLI คือการต่อคำสั่งหลายคำสั่งเข้าด้วยกันในลำดับบนบรรทัดคำสั่ง:
command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;
คำสั่งเหล่านี้รันทุกตัวโดยไม่มีปัญหา ตราบใดที่ไม่มีข้อผิดพลาดเกิดขึ้น แต่เกิดอะไรขึ้นเมื่อมีข้อผิดพลาด? คุณสามารถคาดการณ์และอนุญาตข้อผิดพลาดได้ โดยใช้ตัวดำเนินการควบคุมในตัวของ Bash คือ && และ || ตัวดำเนินการควบคุม 2 ตัวนี้ให้การควบคุมโฟลว์ (Flow Control) บางส่วน และช่วยให้คุณเปลี่ยนลำดับการรันโค้ดได้ เซมิโคลอนก็ถือเป็นตัวดำเนินการควบคุม Bash เช่นกัน รวมถึงตัวอักขระบรรทัดใหม่ (Newline Character)
ตัวดำเนินการ && หมายถึง "ถ้า command1 สำเร็จ ให้รัน command2 ถ้า command1 ล้มเหลวด้วยเหตุผลใดก็ตาม ให้ข้าม command2" ไวยากรณ์เป็นแบบนี้:
command1 && command2
ลองดูคำสั่งที่จะสร้างไดเรกทอรีใหม่ และถ้าสำเร็จ ให้ทำให้เป็นไดเรกทอรีทำงานปัจจุบัน (PWD) ให้แน่ใจว่าไดเรกทอรีบ้าน (~) เป็น PWD ลองก่อนใน /root ซึ่งคุณไม่มีสิทธิ์เข้าถึง:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
mkdir: cannot create directory '/root/testdir/': Permission denied
[student@studentvm1 ~]$
ข้อผิดพลาดถูกปล่อยออกมาจากคำสั่ง mkdir คุณไม่ได้รับข้อผิดพลาดว่าสร้างไฟล์ไม่ได้ เพราะการสร้างไดเรกทอรีล้มเหลว ตัวดำเนินการควบคุม && ตรวจจับรหัสคืนค่า (Return Code) ที่ไม่เป็นศูนย์ ดังนั้นคำสั่ง touch ถูกข้ามไป การใช้ตัวดำเนินการควบคุม && ป้องกันไม่ให้คำสั่ง touch รัน เพราะมีข้อผิดพลาดในการสร้างไดเรกทอรี การควบคุมโฟลว์โปรแกรม CLI แบบนี้สามารถป้องกันไม่ให้ข้อผิดพลาดสะสมและทำให้สถานการณ์แย่ลง แต่ถึงเวลาที่จะซับซ้อนขึ้นนิดหน่อย
ตัวดำเนินการควบคุม || อนุญาตให้คุณเพิ่มคำสั่งโปรแกรมอีกตัวที่รันเมื่อคำสั่งโปรแกรมแรกคืนรหัสมากกว่าศูนย์ ไวยากรณ์พื้นฐานเป็นแบบนี้:
command1 || command2
ไวยากรณ์นี้อ่านว่า "ถ้า command1 ล้มเหลว ให้รัน command2" ซึ่งสื่อให้เข้าใจว่า ถ้า command1 สำเร็จ command2 จะถูกข้าม ลองทดสอบโดยพยายามสร้างไดเรกทอรีใหม่:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$
นี่คือสิ่งที่คาดไว้พอดี เพราะสร้างไดเรกทอรีใหม่ไม่ได้ คำสั่งแรกล้มเหลว ทำให้คำสั่งที่สองรัน
การรวมตัวดำเนินการทั้งสองให้ผลดีที่สุด ไวยากรณ์ตัวดำเนินการควบคุมที่ใช้การควบคุมโฟลว์บางส่วน เมื่อใช้ตัวดำเนินการควบคุม && และ || มีรูปแบบทั่วไปแบบนี้:
preceding commands ; command1 && command2 || command3 ; following commands
ไวยากรณ์นี้อธิบายได้ว่า: "ถ้า command1 ออกด้วยรหัสคืน 0 ให้รัน command2 มิฉะนั้นรัน command3" ลองทดสอบ:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$
ตอนนี้ ลองคำสั่งล่าสุดอีกครั้ง แต่ใช้ไดเรกทอรีบ้านแทน /root คุณจะมีสิทธิ์สร้างไดเรกทอรีนี้:
[student@studentvm1 ~]$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
[student@studentvm1 testdir]$
ไวยากรณ์ตัวดำเนินการควบคุม เช่น command1 && command2 ทำงานได้ เพราะทุกคำสั่งส่งรหัสคืน (Return Code / RC) ไปยังเชลล์ บ่งบอกว่าทำสำเร็จหรือมีความล้มเหลวระหว่างรัน ตามข้อตกลง RC 0 (ศูนย์) แสดงความสำเร็จ และจำนวนบวกใดๆ แสดงความล้มเหลวบางประการ เครื่องมือบางอย่างคืนแค่ 1 เพื่อบ่งบอกความล้มเหลว แต่มากมายใช้รหัสอื่นๆ เพื่อระบุประเภทความล้มเหลว
ตัวแปรเชลล์ Bash $? มี RC จากคำสั่งล่าสุด RC นี้ตรวจสอบได้ง่ายโดยสคริปต์ คำสั่งถัดไปในรายการ หรือแม้แต่ซิสแอดมินโดยตรง เริ่มด้วยการรันคำสั่งง่ายแล้วตรวจสอบ RC ทันที RC จะเป็นของคำสั่งล่าสุดที่รันก่อนที่คุณจะดูมันเสมอ
[student@studentvm1 testdir]$ ll ; echo "RC = $?"
total 1264
drwxrwxr-x 2 student student 4096 Mar 2 08:21 chapter25
drwxrwxr-x 2 student student 4096 Mar 21 15:27 chapter26
-rwxr-xr-x 1 student student 92 Mar 20 15:53 TestFile1
<snip>
drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student 4096 Dec 22 13:15 Videos
RC = 0
[student@studentvm1 testdir]$
RC ในกรณีนี้คือศูนย์ หมายถึงคำสั่งทำสำเร็จ ตอนนี้ลองคำสั่งเดียวกันกับไดเรกทอรีบ้านของ root ซึ่งคุณไม่มีสิทธิ์:
[student@studentvm1 testdir]$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 testdir]$
ในกรณีนี้ RC เป็น 2 หมายถึงการเข้าถึงถูกปฏิเสธสำหรับผู้ใช้ที่ไม่ใช่ root เพื่อเข้าถึงไดเรกทอรีที่ผู้ใช้ไม่ได้รับอนุญาต ตัวดำเนินการควบคุมใช้ RC เหล่านี้ เพื่อให้คุณเปลี่ยนลำดับการรันโปรแกรมได้
สรุป
บทความนี้มองที่ Bash ในฐานะภาษาโปรแกรมมิ่ง และสำรวจไวยากรณ์พื้นฐาน รวมถึงเครื่องมือพื้นฐานบางอย่าง แสดงวิธีพิมพ์ข้อมูลไป STDOUT และวิธีใช้ตัวแปรและตัวดำเนินการควบคุม บทความถัดไปในชุดนี้จะดูตัวดำเนินการตรรกะของ Bash หลายตัว ที่ควบคุมโฟลว์การรันคำสั่ง