Bash เป็น Programming Language อันทรงพลัง ออกแบบมาสำหรับ Command Line และ Shell Scripts บทความ 3 ส่วนนี้ Based on 3-volume Linux Self-study Course สำรวจการใช้ Bash เป็น Programming Language บน CLI
บทความแรก สำรวจ Simple Command-line Programming ด้วย Bash รวม Variables และ Control Operators บทความที่ 2 ดู Types ของ File, String, Numeric, และ Miscellaneous Logical Operators ให้ Execution-flow Control Logic และ Shell Expansions ต่างๆ ใน Bash บทความที่ 3 (และสุดท้าย) สำรวจการใช้ Loops สำหรับ Iterative Operations ต่างๆ และวิธี Control Loops เหล่านั้น
Loops
Programming Language ทุกตัวที่เคยใช้ มี Loop Structures อย่างน้อย 2 Types ให้ Capabilities ต่างๆ ทำ Repetitive Operations ใช้ for Loop บ่อย แต่ while และ until Loops ก็ Useful
For Loops
Implementation ของ for Command ใน Bash ไพ่ Flexible กว่า Most เห็นผม เพราะ Handle Non-numeric Values ได้ ตรงข้าม C Language for Loop Standard จัดการได้แค่ Numeric Values
Basic Structure ของ Bash for Command เรียบง่าย:
for Var in list1 ; do list2 ; done
แปลว่า: "สำหรับแต่ละ Value ใน list1 ตั้ง $Var เป็น Value นั้น แล้ว Perform Program Statements ใน list2 ด้วย Value นั้น เมื่อ Values ใน list1 ใช้หมด เสร็จสิ้น Exit Loop" Values ใน list1 เป็น Explicit String of Values ธรรมดา หรือ Result ของ Command Substitution (อธิบายในบทความที่ 2) ใช้ Construct นี้บ่อย
ลอง ทำให้ ~/testdir ยังเป็น PWD Clean Directory แล้วดู Trivial Example for Loop เริ่ม Explicit List of Values List นี้ Mix Alphanumeric Values — แต่อย่าลืม Variables ทั้งหมดเป็น Strings Treat ได้เช่นกัน
[student@studentvm1 testdir]$ rm *
[student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do echo $I ; done
a
b
c
d
1
2
3
4
Version ใช้งานมากกว่า Variable Name Meaningful:
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Department $Dept" ; done
Department Human Resources
Department Sales
Department Finance
Department Information Technology
Department Engineering
Department Administration
Department Research
สร้าง Directories (แสดง Progress ขณะทำ):
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done
Working on Department Human Resources
Working on Department Sales
Working on Department Finance
Working on Department Information Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Administration
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Engineering
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Finance
drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Human Resources'
drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Information Technology'
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Research
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Sales
$Dept ต้องอยู่ใน Quotes ใน mkdir Statement ไม่เช่นนั้น Two-part Department Names (อย่าง "Information Technology") จะถูก Treat เป็น 2 Departments แยก Highlight Best Practice ชอบ: File และ Directory Names ควรเป็น Single Word 雖然 Modern OS จัดการ Spaces ใน Names ได้ แต่มัน Extra Work สำหรับ Sysadmins ให้ Special Cases นั้น Considered ใน Scripts และ CLI Programs (เกือบแน่นอนควร Considered แม้ Annoying เพราะไม่รู้ว่าจะมี Files อะไร)
ลบทั้งหมดใน ~/testdir — อีกครั้ง — และทำอีกครั้ง:
[student@studentvm1 testdir]$ rm -rf * ; ll
total 0
[student@studentvm1 testdir]$ for Dept in Human-Resources Sales Finance Information-Technology Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done
Working on Department Human-Resources
Working on Department Sales
Working on Department Finance
Working on Department Information-Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Administration
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Engineering
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Human-Resources
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Information-Technology
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Research
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Sales
สมมติมีคนขอ List RPMs ทั้งหมดบน Linux Computer พร้อม Short Description แต่ละตัว เกิดขึ้นตอนทำงาน State of North Carolina Open Source ไม่ "Approved" สำหรับ State Agencies ตอนนั้น ผมใช้ Linux Desktop Only PHBs (Pointy-haired Bosses) ต้องการ List แต่ละ Software Installed บน Computer ผม เพื่อ "Approve" Exception
จะ Approach ยังไง? วิธีหนึ่ง เริ่มจาก Knowledge ว่า rpm –qa ให้ Complete Description ของ RPM รวม 2 Items ที่ PHBs ต้องการ: Software Name และ Brief Summary
Build Up Final Result ทีละขั้นตอน แรก List All RPMs:
[student@studentvm1 testdir]$ rpm -qa
perl-HTTP-Message-6.18-3.fc29.noarch
perl-IO-1.39-427.fc29.x86_64
perl-Math-Complex-1.59-429.fc29.noarch
lua-5.3.5-2.fc29.x86_64
java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64
util-linux-2.32.1-1.fc29.x86_64
libreport-fedora-2.9.7-1.fc29.x86_64
rpcbind-1.2.5-0.fc29.x86_64
libsss_sudo-2.0.0-5.fc29.x86_64
libfontenc-1.1.3-9.fc29.x86_64
<snip>
เพิ่ม sort และ uniq Commands Sort List และ Print Unique Ones (เพราะ RPMs ชื่อเหมือนกันอาจ Installed):
[student@studentvm1 testdir]$ rpm -qa | sort | uniq
a2ps-4.14-39.fc29.x86_64
aajohan-comfortaa-fonts-3.001-3.fc29.noarch
abattis-cantarell-fonts-0.111-1.fc29.noarch
abiword-3.0.2-13.fc29.x86_64
abrt-2.11.0-1.fc29.x86_64
abrt-addon-ccpp-2.11.0-1.fc29.x86_64
abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64
abrt-addon-kerneloops-2.11.0-1.fc29.x86_64
abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64
abrt-addon-vmcore-2.11.0-1.fc29.x86_64
<snip>
เนื่องจากได้ List RPMs ที่ถูกต้อง ใช้เป็น Input List ให้ Loop Print Details ทุก RPM:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done
Code นี้ Produce Data มากกว่าที่ต้องการ Note Loop Complete Next Step Extract เฉพาะ Info ที่ PHBs ขอ เพิ่ม egrep Command Select ^Name หรือ ^Summary Caret (^) Specify Beginning of Line ดังนั้น Line ที่ Name หรือ Summary อยู่ต้นบรรทัดจะ Display
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary"
Name : a2ps
Summary : Converts text and other types of files to PostScript
Name : aajohan-comfortaa-fonts
Summary : Modern style true type font
Name : abattis-cantarell-fonts
Summary : Humanist sans serif font
Name : abiword
Summary : Word processing program
Name : abrt
Summary : Automatic bug detection and reporting tool
<snip>
ลอง grep แทน egrep ในคำสั่งข้างบน จะไม่ Work สามารถ Pipe Output ผ่าน less Filter Explore Results Final Command Sequence:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" > RPM-summary.txt
CLI Program นี้ใช้ Pipelines, Redirection, และ for Loop — ทั้งหมดบรรทัดเดียว Redirect Output ของ CLI Program ไป File ใช้ใน Email หรือ Input สำหรับ Purposes อื่น
Process Build Up Program ทีละขั้นตอน ให้เห็น Results แต่ละ Step Ensure Working ตามคาด และให้ Desired Results
จาก Exercise นี้ PHBs ได้ List กว่า 1,900 RPM Packages แยกกัน ไม่อาจใครอ่าน List นั้น แต่ให้พวกเขา Exactly What They Asked For และไม่ได้ยิน Complaints อีก
Other Loops
อีก 2 Loop Structures ใน Bash: while และ until คล้ายกันมากทั้ง Syntax และ Function Basic Syntax:
while [ expression ] ; do list ; done
และ
until [ expression ] ; do list ; done
Logic ตัวแรก: "While Expression Evaluate เป็น True Execute List of Program Statements เมื่อ Expression Evaluate เป็น False Exit Loop" ตัวที่สอง: "Until Expression Evaluate เป็น True Execute List of Program Statements เมื่อ Expression Evaluate เป็น True Exit Loop"
While Loop
while Loop Execute Series of Program Statements While (Solongas) Logical Expression Evaluate เป็น True PWD ควรยังเป็น ~/testdir
Simplest Form while Loop รันตลอดกาล ใช้ True Statement Generate "True" Return Code เสมอ สามารถใช้ "1" ง่ายๆ ก็ได้ แต่นี้ Illustrate True Statement:
[student@studentvm1 testdir]$ X=0 ; while [ true ] ; do echo $X ; X=$((X+1)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 testdir]$
CLI Program นี้ควร Make Sense ตอนนี้รู้ Parts แรก Set $X เป็น Zero เผื่อค่า Left Over จาก Program/CLI ก่อนหน้า แล้ว Logical Expression [ true ] Always Evaluate to 1 (True) List Instructions ระหว่าง do และ done Execute Forever — หรือจนกด Ctrl+C หรือส่ง Signal 2 Instructions คือ Arithmetic Expansion Print Current Value $X แล้ว Increment 1
Tenet ของ The Linux Philosophy for Sysadmins คือ Strive for Elegance วิธีหนึ่งคือ Simplicity ลด Complexity ได้ด้วย Variable Increment Operator ++ Instance แรก Print Current Value แล้ว Increment Variable วาง ++ หลัง Variable:
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
ลบ | head ท้าย Program รันอีกครั้ง
Version นี้ Variable Increment ก่อน Print Value วาง ++ หน้า Variable มองเห็นความต่างไหม?
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((++X)) ; done | head
1
2
3
4
5
6
7
8
9
ลด 2 Statements เหลือ 1 Print Value และ Increment Value มี Decrement Operator -- ด้วย
ต้องการ Method Stop Loop ที่ Specific Number เปลี่ยน True Expression เป็น Actual Numeric Evaluation Expression Loop ถึง 5 หยุด ใน Code ด้านล่าง -le Logical Numeric Operator "Less Than or Equal To" หมายถึง: "Solongas $X <= 5 Loop Continue เมื่อ $X Increment เป็น 6 Loop Terminate"
[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo $((X++)) ; done
0
1
2
3
4
5
[student@studentvm1 ~]$
Until Loop
until Command คล้าย while มาก ความต่างคือ Continue Loop จน Logical Expression Evaluate เป็น "True" ดู Simplest Form:
[student@studentvm1 ~]$ X=0 ; until false ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 ~]$
ใช้ Logical Comparison นับถึงค่าเฉพาะ:
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((X++)) ; done
0
1
2
3
4
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((++X)) ; done
1
2
3
4
5
[student@studentvm1 ~]$
สรุป
Series นี้สำรวจ Powerful Tools หลายอย่าง Build Bash Command-line Programs และ Shell Scripts แตเพียง Scratch Surface ของ Interesting Things ทำได้กับ Bash ที่เหลือขึ้นอยู่กับคุณ
ค้นพบว่า Best Way เรียน Bash Programming คือ ทำมัน หา Simple Project ต้อง Multiple Bash Commands ทำเป็น CLI Program Sysadmins ทำ Tasks หลายอย่าง Lend Themselves ให้ CLI Programming มั่นใจว่าจะหา Tasks Automate ได้ง่าย
หลายปีก่อน รู้จัก Shell Languages อื่นและ Perl ตัดสินใจใช้ Bash สำหรับ Sysadmin Automation Tasks ทั้งหมด ค้นพบว่า — บางครั้ง Search เล็กน้อย — สามารถใช้ Bash Accomplish Everything ที่ต้องการ