ในบทความแรกของชุดนี้ คุณสร้างสคริปต์ Bash เล็กมาก หนึ่งบรรทัด และสำรวจเหตุผลในการสร้างเชลล์สคริปต์ และว่าทำไมมันถึงเป็นตัวเลือกที่มีประสิทธิภาพที่สุดสำหรับซิสแอดมิน แทนโปรแกรมที่คอมไพล์ (Compiled Programs) ในบทความที่สอง คุณเริ่มงานสร้างเทมเพลตที่ค่อนข้างง่าย ที่คุณสามารถใช้เป็นจุดเริ่มต้นสำหรับโปรแกรม Bash อื่นๆ จากนั้นสำรวจวิธีทดสอบมัน
บทความที่สามของ 4 บทความในชุดนี้ อธิบายวิธาสร้างและใช้ฟังก์ชัน Help เรียบง่าย ขณะสร้างสิ่งช่วยเหลือ (Help Facility) ของคุณ คุณจะเรียนรู้เกี่ยวกับการใช้ฟังก์ชัน และวิธาจัดการตัวเลือกบรรทัดคำสั่ง เช่น -h
ทำไมต้อง Help?
แม้โปรแกรม Bash ที่ค่อนข้างง่าย ควรมีสิ่งช่วยเหลือ (Help Facility) บางอย่าง แม้จะค่อนข้างพื้นฐาน โปรแกรม Bash หลายตัวที่ผมเขียน ใช้บ่อยน้อยมาก จนผมลืมไวยากรณ์คำสั่งที่ต้องการอย่างแม่นยำ อื่นๆ ซับซ้อนมาก จนผมต้องทบทวนตัวเลือกและอาร์กิวเมนต์ แม้ใช้บ่อย
การมีฟังก์ชัน Help ในตัว (Built-in) ช่วยให้คุณดูสิ่งเหล่านั้นได้โดยไม่ต้องตรวจสอบโค้ดเอง Help Facility ที่ดีและสมบูรณ์ ยังเป็นส่วนหนึ่งของเอกสารโปรแกรม (Program Documentation)
เกี่ยวกับฟังก์ชัน (Functions)
ฟังก์ชันเชลล์ (Shell Functions) คือ รายการคำสั่งโปรแกรม Bash ที่เก็บไว้ในสภาพแวดล้อมของเชลล์ และสามารถรันได้ เช่นคำสั่งอื่นๆ โดยพิมพ์ชื่อที่บรรทัดคำสั่ง ฟังก์ชันเชลล์อาจรู้จักในชื่อ Procedures หรือ Subroutines ขึ้นอยู่กับภาษาโปรแกรมอื่นที่คุณใช้
ฟังก์ชันถูกเรียกในสคริปต์หรือจาก CLI โดยใช้ชื่อของมัน เหมือนคำสั่งอื่นๆ ในโปรแกรม CLI หรือสคริปต์ คำสั่งในฟังก์ชันจะรันเมื่อถูกเรียก แล้วลำดับโฟลว์โปรแกรมจะกลับไปยังผู้เรียก (Calling Entity) และชุดคำสั่งโปรแกรมถัดไปใน Entity นั้นจะรัน
ไวยากรณ์ของฟังก์ชันคือ:
FunctionName(){program statements}
สำรวจโดยสร้างฟังก์ชันง่ายๆ ที่ CLI (ฟังก์ชันถูกเก็บในสภาพแวดล้อมเชลล์สำหรับอินสแตนซ์เชลล์ที่สร้างมัน) คุณจะสร้างฟังก์ชันชื่อ hw ย่อมาจาก "hello world" ป้อนโค้ดต่อไปนี้ที่ CLI และกด Enter จากนั้นป้อน hw เหมือนคำสั่งเชลล์ทั่วไป:
[student@testvm1 ~]$ hw(){ echo "Hi there kiddo"; }
[student@testvm1 ~]$ hw
Hi there kiddo
[student@testvm1 ~]$
โอเค ผมเบื่อ "Hello world" มาตรฐานหน่อย ตอนนี้ ลิสต์ฟังก์ชันที่กำหนดอยู่ทั้งหมด มีเยอะมาก ผมแสดงแค่ฟังก์ชัน hw ใหม่ เมื่อถูกเรียกจากบรรทัดคำสั่งหรือในโปรแกรม ฟังก์ชันจะทำงานที่เขียนไว้ แล้วออกและคืนการควบคุมให้ผู้เรียก บรรทัดคำสั่ง หรือคำสั่งโปรแกรม Bash ถัดไปในสคริปต์หลังคำสั่งเรียก:
[student@testvm1 ~]$ declare -f | less
<snip>
hw ()
{
echo "Hi there kiddo"
}
<snip>
ลบฟังก์ชัันนั้นออก เพราะไม่ต้องการแล้ว ทำได้ด้วยคำสั่ง unset:
[student@testvm1 ~]$ unset -f hw ; hw
bash: hw: command not found
[student@testvm1 ~]$
สร้างฟังก์ชัน Help
เปิดโปรแกรม hello ใน Editor และเพิ่มฟังก์ชัน Help ด้านล่าง ลงในโค้ดโปรแกรม hello หลังคำสั่งลิขสิทธิ์ (Copyright Statement) แต่ก่อนคำสั่ง echo "Hello world!" ฟังก์ชัน Help นี้จะแสดงคำอธิบายสั้นของโปรแกรม ไดอะแกรมไวยากรณ์ (Syntax Diagram) และคำอธิบายสั้นของตัวเลือกที่มีอยู่ เพิ่มการเรียกฟังก์ชัน Help เพื่อทดสอบ และบรรทัดคอมเมนต์บางบรรทัดที่ให้ขอบเขตการมองเห็น (Visual Demarcation) ระหว่างฟังก์ชันและส่วนหลักของโปรแกรม:
################################################################################
# Help #
################################################################################
Help()
{
# Display Help
echo "Add description of the script functions here."
echo
echo "Syntax: scriptTemplate [-g|h|v|V]"
echo "options:"
echo "g Print the GPL license notification."
echo "h Print this Help."
echo "v Verbose mode."
echo "V Print software version and exit."
echo
}
################################################################################
################################################################################
# Main program #
################################################################################
################################################################################
Help
echo "Hello world!"
ตัวเลือกที่อธิบายในฟังก์ชัน Help นี้ เป็นตัวเลือกทั่วไปสำหรับโปรแกรมที่ผมเขียน แม้ยังไม่มีในโค้ด รันโปรแกรมเพื่อทดสอบ:
[student@testvm1 ~]$ ./hello
Add description of the script functions here.
Syntax: scriptTemplate [-g|h|v|V]
options:
g Print the GPL license notification.
h Print this Help.
v Verbose mode.
V Print software version and exit.
Hello world!
[student@testvm1 ~]$
เพราะคุณยังไม่ได้เพิ่มตรรกะแสดง Help เฉพาะตอนต้องการ โปรแกรมจะแสดง Help ทุกครั้ง เนื่องจากฟังก์ชันทำงานถูกต้อง อ่านต่อเพื่อเพิ่มตรรกะบางอย่าง เพื่อแสดง Help เฉพาะเมื่อใช้ตัวเลือก -h ตอนเรียกโปรแกรมที่บรรทัดคำสั่ง
จัดการตัวเลือก (Handling Options)
ความสามารถของสคริปต์ Bash ในการจัดการตัวเลือกบรรทัดคำสั่ง เช่น -h ให้ความสามารถทรงพลังบางอย่าง เพื่อควบคุมโปรแกรมและปรับเปลี่ยนสิ่งที่มันทำ ในกรณีของตัวเลือก -h คุณต้องการให้โปรแกรมพิมพ์ข้อความ Help ไปที่เทอร์มินัล แล้วจบโดยไม่รันส่วนที่เหลือของโปรแกรม ความสามารถในการประมวลผลตัวเลือกที่ป้อนบนบรรทัดคำสั่ง สามารถเพิ่มลงในสคริปต์ Bash ได้ โดยใช้คำสั่ง while (ดู How to program with Bash: Loops เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ while) ร่วมกับคำสั่ง getopts และ case
คำสั่ง getopts อ่านตัวเลือกทั้งหมดที่ระบุบนบรรทัดคำสั่ง และสร้างรายการตัวเลือกเหล่านั้น ในโค้ดด้านล่าง คำสั่ง while วนลูปผ่านรายการตัวเลือก โดยตั้งค่าตัวแปร $options สำหรับแต่ละตัว คำสั่ง case ใช้ประเมินตัวเลือกแต่ละตัวทีละตัว และรันคำสั่งใน Stanza (ส่วน) ที่ตรงกัน คำสั่ง while จะประเมินรายการตัวเลือกต่อไป จนกว่าจะประมวลผลหมด หรือเจอคำสั่ง exit ซึ่งจบโปรแกรม
อย่าลืมลบการเรียกฟังก์ชัน Help ก่อนคำสั่ง echo "Hello world!" เพื่อให้ Main Body ของโปรแกรมเป็นแบบนี้:
################################################################################
################################################################################
# Main program #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed. #
################################################################################
# Get the options
while getopts ":h" option; do
case $option in
h) # display Help
Help
exit;;
esac
done
echo "Hello world!"
สังเกตเซมิโคลอนคู่ (Double Semicolon) ท้ายคำสั่ง exit ใน case option สำหรับ -h นี้จำเป็นสำหรับแต่ละตัวเลือกที่เพิ่มใน case statement นี้ เพื่อแบ่งขอบเขตสิ้นสุดของแต่ละตัวเลือก
การทดสอบ (Testing)
การทดสอบตอนนี้ซับซ้อนขึ้นนิดหน่อย คุณต้องทดสอบโปรแกรมกับตัวเลือกที่ต่างกันหลายตัว—และไม่มีตัวเลือก—เพื่อดูว่ามันตอบสนองอย่างไร ก่อน ทดสอบโดยไม่มีตัวเลือก เพื่อให้แน่ใจว่าพิมพ์ "Hello world!" ตามที่ควร:
[student@testvm1 ~]$ ./hello
Hello world!
ทำงานได้ ดังนั้นทดสอบตรรกะที่แสดงข้อความ Help:
[student@testvm1 ~]$ ./hello -h
Add description of the script functions here.
Syntax: scriptTemplate [-g|h|t|v|V]
options:
g Print the GPL license notification.
h Print this Help.
v Verbose mode.
V Print software version and exit.
ทำงาน ตามที่คาดไว้ ลองทดสอบดูว่าเกิดอะไรขึ้นเมื่อป้อนตัวเลือกที่ไม่คาดคิด:
[student@testvm1 ~]$ ./hello -x
Hello world!
[student@testvm1 ~]$ ./hello -q
Hello world!
[student@testvm1 ~]$ ./hello -lkjsahdf
Add description of the script functions here.
Syntax: scriptTemplate [-g|h|t|v|V]
options:
g Print the GPL license notification.
h Print this Help.
v Verbose mode.
V Print software version and exit.
[student@testvm1 ~]$
โปรแกรมเพียงข้ามตัวเลือกที่ไม่มีการตอบสนองเฉพาะ โดยไม่สร้างข้อผิดพลาดใดๆ แต่สังเกตรายการสุดท้าย (ด้วย -lkjsahdf สำหรับตัวเลือก): เพราะมี h ในรายการตัวเลือก โปรแกรมจดจำและพิมพ์ข้อความ Help การทดสอบนี้แสดงว่า โปรแกรมยังไม่มีความสามารถจัดการอินพุตที่ผิดและจบโปรแกรมหากตรวจพบ
คุณสามารถเพิ่ม Stanza case อีกตัวเข้าไปในคำสั่ง case เพื่อจับคู่ตัวเลือกใดๆ ที่ไม่มีการจับคู่ชัดเจน Case ทั่วไป (General Case) นี้จะจับคู่ทุกอย่างที่คุณไม่ได้จัดให้จับคู่เฉพาะ Case statement ตอนนี้เป็นแบบนี้ โดยมีการจับคู่ทั่วไป \? เป็น case สุดท้าย Case เฉพาะเจาะจงเพิ่มเติม ต้องมาก่อน case สุดท้ายนี้เสมอ:
while getopts ":h" option; do
case $option in
h) # display Help
Help
exit;;
\?) # incorrect option
echo "Error: Invalid option"
exit;;
esac
done
ทดสอบโปรแกรมอีกครั้ง ใช้ตัวเลือกเดียวกับก่อนหน้า และดูว่าทำงานตอนนี้อย่างไร
คุณอยู่ที่ไหน (Where You Are)
คุณทำสำเร็จมากในบทความนี้ โดยเพิ่มความสามารถประมวลผลตัวเลือกบรรทัดคำสั่ง และ Procedure Help สคริปต์ Bash ของคุณตอนนี้เป็นแบบนี้:
#!/usr/bin/bash
################################################################################
# scriptTemplate #
# #
# Use this template as the beginning of a new program. Place a short #
# description of the script here. #
# #
# Change History #
# 11/11/2019 David Both Original code. This is a template for creating #
# new Bash shell scripts. #
# Add new history entries as needed. #
# #
# #
################################################################################
################################################################################
################################################################################
################################################################################
# #
# Copyright (C) 2007, 2019 David Both #
# LinuxGeek46@both.org #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the Free Software #
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
# #
################################################################################
################################################################################
################################################################################
################################################################################
# Help #
################################################################################
Help()
{
# Display Help
echo "Add description of the script functions here."
echo
echo "Syntax: scriptTemplate [-g|h|t|v|V]"
echo "options:"
echo "g Print the GPL license notification."
echo "h Print this Help."
echo "v Verbose mode."
echo "V Print software version and exit."
echo
}
################################################################################
################################################################################
# Main program #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed. #
################################################################################
# Get the options
while getopts ":h" option; do
case $option in
h) # display Help
Help
exit;;
\?) # incorrect option
echo "Error: Invalid option"
exit;;
esac
done
echo "Hello world!"
อย่าลืมทดสอบรุ่นนี้ของโปรแกรมอย่างละเอียดมาก ใช้อินพุตสุ่ม และดูว่าจะเกิดอะไรขึ้น คุณควรลองทดสอบตัวเลือกที่ถูกต้องและไม่ถูกต้อง โดยไม่ใช้แดช (-) นำหน้า
ครั้งหน้า (Next Time)
ในบทความนี้ คุณเพิ่มฟังก์ชัน Help รวมถึงความสามารถประมวลผลตัวเลือกบรรทัดคำสั่ง เพื่อแสดงมันได้อย่างเลือกปฏิบัติ โปรแกรมเริ่มซับซ้อนขึ้น ดังนั้นการทดสอบจึงสำคัญขึ้น และต้องการเส้นทางการทดสอบมากขึ้นเพื่อให้ครบถ้วน
บทความถัดไปจะมองที่การเริ่มต้นตัวแปร และการทำ Sanity Checking นิดหน่อย เพื่อให้แน่ใจว่าโปรแกรมจะรันภายใต้ชุดเงื่อนไขที่ถูกต้อง
แหล่งข้อมูล (Resources)
- How to program with Bash: Syntax and tools
- How to program with Bash: Logical operators and shell expansions
- How to program with Bash: Loops
ชุดบทความนี้บางส่วนอิงจาก Volume 2, Chapter 10 ของหลักสูตร Linux เรียนรู้ด้วยตนเอง 3 ส่วน ของ David Both: Using and Administering Linux—Zero to SysAdmin.