โปรแกรมเชลล์ (shell) หรือ โปรแกรมตัวแปรคำสั่ง (interpreter) คือโปรแกรมที่ถูกสร้างมาเพื่อรับคำสั่งจากผู้ใช้งานเพื่อไปสั่งการระบบปฏิบัติการอีกทีหนึ่ง (command line interpreter) โดยโปรแกรมจะเตรียมหน้าจอคอนโซลเพื่อให้ผู้ใช้งานป้อนคำสั่งตามที่ต้องการ เช่น เราป้อนคำสั่งผ่านหน้าจอคอนโซลดังนี้
find . -name “*.c” -ls
ไฟล์สคริป (script file) หรือ เชลล์สคริป (shell script) คือชุดของคำสั่งที่เราจัดเตรียมไว้ล่วงหน้าโดยสร้างเป็นไฟล์ไว้ ในกรณีเราสร้างไฟล์สคริปเพื่อใช้งานกับโปรแกรมเชลล์เรามักจะเรียกว่าเชลล์สคริป (shell script) เช่น เราพิมพ์ข้อความในไฟล์เพื่อให้ทำงานแบบเดียวกับคำสั่งด้านบนได้ดังนี้
#!/bin/bash
find . -name “*.c” -ls
จะเห็นว่านอกจากรองรับการป้อนคำสั่งผ่านหน้าจอคอนโซลแล้ว เราสามารถใช้โปรแกรมเชลล์ในการเรียกเชลล์สคริปมาทำงาน โดยรองรับการทำงานทั้งในแบบที่ต้องมีการตอบโต้กับผู้ใช้งานผ่านหน้าจอคอนโซลและแบบที่ทำงานเป็นเบื้องหลังโดยไม่ต้องมีหน้าจอคอนโซล ชึ่งผลลัพธ์ของการทำงานโดยใช้ไฟล์สคริปจะเหมือนกับที่เราป้อนคำสั่งผ่านหน้าจอคอนโซลนั่นเอง
การใช้งานเชลล์สคริปช่วยให้เราทำงานที่ต้องทำซ้ำๆได้ง่ายขึ้น ยิ่งถ้าเป็นงานที่ซับซ้อนซึ่งถ้าเราต้องใส่คำสั่งทีละคำสั่งในหน้าจอคอนโซลอาจมีโอกาศป้อนคำสั่งผิดพลาดได้ การใช้เชลล์สคริปจึงช่วยให้เราทำงานได้ง่ายขึ้นและลดความผิดพลาดลง
ประโยชน์ของการใช้เชลล์สคริปคือ
- เพื่อให้ระบบทำงานให้เราอย่างอัตโนมัติ ลดข้อผิดพลาด เช่น ให้เชลล์สคริปทำงานแทนเราตามเงื่อนไขที่กำหนด ในทุกๆ 1 ชั่วโมง
- ลดจำนวนคำสั่งให้เหลือเพียงคำสั่งเดียว โดยเราเอาคำสั่งทั้งหมดมาใส่ไว้ในเชลล์สคริปแทน
- สามารถแบ่งปันเชลล์สคริปให้ผู้อื่นใช้ได้ แทนที่จะต้องสอนขั้นตอนการเรียกคำสั่งต่างๆที่ซับซ้อน
- ใช้เพื่อทำเป็นต้นแบบโปรแกรมแบบเร่งด่วนได้ เพราะเชลล์
สคริปไม่ต้องคอมไพล์ก่อนใช้งาน
- ใช้สร้างคำสั่งใหม่ๆโดยการเอาคำสั่งและเครื่องมืออื่นๆมาประกอบกันในเชลล์สคริป
- ใช้สร้างการควบคุมการใช้งานของผู้ใช้งาน เช่น ให้เชลล์สคริปเสนอทางเลือกแก่ผู้ใช้งานในแต่ละขั้นตอนการทำงาน
จากตัวอย่างเชลล์สคริปด้านบน จะเห็นว่าบรรทัดแรกคือ #!/bin/bash ซึ่งเป็นการบอกว่าเราจะใช้โปรแกรมตัวแปรคำสั่ง (interpreter) ตัวใดสำหรับชุดของคำสั่งในเชลล์สคริปของเรา เช่น #!/bin/bash เป็นการบอกว่าเราจะใช้โปรแกรม bash ที่อยู่ภายใต้ไดเร็คทอรี่ bin ในการแปลคำสั่งในเชลล์สคริปของเรา สำหรับการใช้งานลินุกซ์โดยทั่วไปก็จะใช้โปรแกรม bash แต่ก็ยังมีโปรแกรมอื่นให้เลือกใช้ เช่น /bin/sh, /bin/tcsh, /bin/csh และ /bin/ksh เราสามารถดูได้ว่าลินุกซ์ดิสโทรที่เราใช้จัดเตรียมโปรแกรมเชลล์อะไรให้เราใช้บ้างโดยดูได้จากไฟล์ etc/shells
ภาพแสดงตัวอย่างโปรแกรมเชลล์ที่มีในระบบ
นอกจากโปรแกรมเชลล์ที่ใช้ในการสั่งการระบบปฏิบัติการแล้ว ยังมีโปรแกรมเชลล์หรือโปรแกรมตัวแปรคำสั่งสำหรับภาษาโปรแกรมอื่นๆ เช่น /usr/bin/perl – สำหรับโปรแกรมภาษา perl, หรือ /usr/bin/python – สำหรับโปรแกรมภาษา python เป็นต้น
ลองสร้างเชลล์สคริปง่ายๆ
ทีนี้เรามาลองสร้างเชลล์สคริปแบบง่ายๆดู โดยป้อนคำสั่งที่หน้าจอคอนโซลตามตัวอย่างด้านล่าง
cat > helloworld.sh (สร้างไฟล์ใหม่โดยใช้คำสั่ง cat)
#!/bin/bash (เชลล์สคริปจะต้องเริ่มต้นด้วยบรรทัดนี้เสมอเพื่อระบุโปรแกรมเชลล์ที่จะใช้)
echo “Hello World” (ใช้คำสั่ง echo แสดงข้อความที่หน้าจอ)
ตามด้วยกดปุ่ม Enter และ ปุ่ม Ctrl-D เพื่อบันทึกไฟล์ หรือจะสร้างไฟล์ helloworld.sh ด้วยโปรแกรมแก้ไขไฟล์ข้อความที่ถนัดก็ได้ หลังจากนั้นให้แก้ไขคุณสมบัติของไฟล์ให้สามารถ execute ได้สำหรับ all user ด้วยคำสั่ง chmod +x helloworld.sh หรือจะแก้ไขโดยใช้เครื่องมือที่เป็น GUI ก็ได้ เราสามารถสั่งให้โปรแกรมเชลล์อ่านเชลล์สคริปได้ด้วยคำสั่ง ./helloworld.sh หรือใช้คำสั่ง bash helloworld.sh ก็ได้ ถ้าใช้คำสั่ง bash helloworld.sh จะไม่จำเป็นต้องแก้ไขคุณสมบัติของไฟล์ให้สามารถ execute ได้สำหรับ all user ผลลัพธ์ที่ได้คือโปรแกรมจะแสดงข้อความ Hello World
ภาพแสดงตัวอย่างเชลล์สคริป
ลองสร้างเชลล์สคริปแบบที่ต้องมีการโต้ตอบกับผู้ใช้งาน
ทีนี้เราจะมาลองสร้างเชลล์สคริปแบบที่ต้องมีการโต้ตอบกับผู้ใช้งาน โดยแก้ไขเชลล์สคริปจากหัวข้อด้านบนให้ถามชื่อผู้ใช้งานโดยใช้คำสั่ง read และเอาไปเก็บไว้ที่ตัวแปรชื่อ name และเราจะเอาค่าที่เก็บในตัวแปรมาแสดงโดยการใช้เครื่องหมาย $ นำหน้าตัวแปร เช่น $name
ภาพแสดงตัวอย่างเชลล์สคริป
การคืนค่าของเชลล์สคริป
เมื่อเราเรียกโปรแกรมเชลล์ให้มาอ่านเชลล์สคริปเพื่อดำเนินการ โปรแกรมเชลล์จะสร้างสิ่งแวดล้อมเพื่อการทำงานสำหรับเชลล์
สคริปนั้นๆขึ้นมาหรือจะเรียกว่าโปรเซสก็ได้ เราสามารถกำหนดให้เชลล์สคริปคืนค่ากลับออกมา (return value) หลังจากทำงานเสร็จสิ้น ได้โดยใช้คำสั่ง exit การคืนค่าจะช่วยให้โปรเซสอื่นสามารถรับรู้ว่า
โปรเซสนี้ได้ทำงานเสร็จสิ้นแล้ว โดยเฉพาะโปรเซสที่ต้องทำงานสัมพันธ์กัน เพื่อที่จะได้ส่งต่อการดำเนินการได้อย่างถูกต้อง
เมื่อเชลล์สคริปทำงาน เราสามารถตรวจสอบค่าหรือเงื่อนไขที่ต้องการได้เพื่อที่จะคืนค่าว่าเชลล์สคริปทำงานสำเร็จหรือล้มเหลวได้ โดยมากมักจะคืนค่า 0 หากเชลล์สคริปทำงานสำเร็จ หรือ เป็นค่าอื่นๆหากเชลล์สคริปทำงานล้มเหลว ตัวอย่างเช่นเราจะดูค่าจากการเรียกใช้คำสั่ง ls ในการค้นหาไฟล์ว่าพบหรือไม่ ซึ่งค่าที่ตอบกลับมาจะถูกเก็บไว้ที่ตัวแปร ? และเราสามารถเรียกดูได้โดยใช้คำสั่ง echo $?
ภาพตัวอย่างการดูค่า $?
ไวยากรณ์และอักขระพิเศษที่ใช้ในการเขียนเชลล์สคริป
การเขียนเชลล์สคริปจะมีไวยากรณ์และกฏที่เราต้องปฏิบัติตามไม่ว่าจะเป็นการกำหนดตัวแปร โครงสร้างของคำสั่ง หรือ รูปแบบคำสั่งที่ใช้ได้ ตัวอย่างของอักขระพิเศษและการใช้งานพื้นฐานมีดังนี้
การเพิ่มคอมเมนต์หรือบอกว่าใช้โปรแกรมตัวแปรคำสั่งตัวไหน
ในการเพิ่มคอมเมนต์หรือบอกว่าใช้โปรแกรมตัวแปรคำสั่งตัวไหน เราจะใช้อักขระ # ที่จุดเริ่มต้นของบรรทัดที่เป็นคอมเมนต์ ยกเว้นเมื่อใช้ในรูปแบบ \# หรือ #! ในบรรทัดแรกของเชลล์สคริปเพื่อบอกว่าจะใช้โปรแกรมตัวแปรคำสั่งตัวไหน ตัวอย่างเช่น
#!/bin/bash (เชลล์สคริปจะต้องเริ่มต้นด้วยบรรทัดนี้เสมอเพื่อระบุโปรแกรมเชลล์ที่จะใช้)
#this is comment line (บรรทัดที่เป็นคอมเมนต์)
echo “Hello World” (ใช้คำสั่ง echo แสดงข้อความที่หน้าจอ)
การแบ่งบรรทัดยาวๆออกเป็นหลายบรรทัด
ในการการแบ่งบรรทัดยาวๆออกเป็นหลายบรรทัด เราจะใช้อักขระ \ ( concatenation operator) ที่ท้ายสุดของบรรทัดเพื่อบอกว่าข้อความยังมีต่อในบรรทัดถัดไป เช่น หากคำสั่งมีขนาดยาวมากจนไม่สะดวกที่จะเขียนในบรรทัดเดียว ดังตัวอย่าง
scp abc@server1.linux.com:/var/ftp/pub/userdata/custdata/read \
abc@server3.linux.co.in:/opt/oradba/master/abc/
จากตัวอย่างเป็นการใช้คำสั่ง scp เพื่อสำเนาไฟล์จากเซอร์ฟเวอร์นึงไปยังอีกเซอร์ฟเวอร์นึงซึ่งพาธในคำสั่งยาวมากจึงแบ่งออกเป็น 2 บรรทัดเพื่อให้อ่านง่ายขึ้น เมื่อโปรแกรมเชลล์อ่านบรรทัดแรกมาจนถึงอักขระ \ ก็จะอ่านบรรทัดถัดไปแล้วเอามารวมเป็นคำสั่งเดียวกัน
การเชื่อมคำสั่งหลายๆคำสั่งต่อกัน (chaning of command)
ในบางครั้งเราอาจต้องการเชื่อมคำสั่งหลายๆคำสั่งให้ทำงานต่อๆกันไป โดยเราสามารถกำหนดให้คำสั่งทำงานต่อๆกันไปได้ทั้งแบบมีเงื่อนไขหรือไม่มีเงื่อนไขตามแต่ว่าเราใช้อักขระใดเป็นตัวดำเนินการ ดังนี้
ในการเชื่อมต่อให้คำสั่งทำงานต่อๆกันไป โดยไม่สนใจว่าคำสั่งก่อนหน้าทำงานสำเร็จหรือไม่ เราใช้อักขระ ; เป็นตัวเชื่อมต่อ การเชื่อมต่อโดยใช้อักขระ ; จะเหมือนกับการเขียนคำสั่งเรียงลำดับกันไปทีละบรรทัด ตัวอย่างเช่น
make ; make install ; make clean (สมมุติว่าคำสั่ง make ไม่สำเร็จจะยังคงทำคำสั่ง make install ต่อไป)
แต่ถ้าเราต้องการยกเลิกการทำงานของคำสั่งถัดไปหากคำสั่งก่อนหน้าทำงานไม่สำเร็จ เราใช้อักขระ && เป็นตัวเชื่อมต่อ ตัวอย่างเช่น
make && make install && make clean (สมมุติว่าคำสั่ง make ไม่สำเร็จจะไม่ทำคำสั่งต่อไป และเลื่อนไปบรรทัดถัดไป)
หากเราต้องการให้คำสั่งทำงานต่อๆกันไปจนกว่าจะพบคำสั่งที่ทำงานสำเร็จ เราใช้อักขระ || เป็นตัวเชื่อมต่อ ตัวอย่างเช่น
cat file1 || cat file2 || cat file3 (สมมุติว่าทำคำสั่งที่หนึ่งไม่สำเร็จจะทำคำสั่งที่สองต่อ แต่เมื่อคำสั่งที่สองสำเร็จจะไม่ทำคำสั่งที่สาม)
การเปลี่ยนทิศทางของเอาท์พุต
ระบบปฏิบัติการโดยทั่วไปจะรับอินพุตจากคีย์บอร์ดและส่งเอาท์พุตออกทางจอ แต่ด้วยโปรแกรมเชลล์เราสามารถส่งเอาท์พุตไปยังไฟล์ได้ ซึ่งเราเรียกว่าการเปลี่ยนทิศทาง (redirect) ของเอาท์พุต ในการเปลี่ยนทิศทางของเอาท์พุตเราใช้อักขระ > ตัวอย่างเช่น การเปลี่ยนเอาท์พุตไปยังไฟล์เมื่อใช้คำสั่ง free แทนที่จะแสดงบนจอดังนี้
free > /tmp/free.out
จากตัวอย่างด้านบน ผลลัพธ์ที่ได้จากคำสั่ง free จะถูกเขียนลงในไฟล์ชื่อ free.out ไฟล์นี้จะถูกสร้างขึ้นมาหากยังไม่มีในระบบ และเราสามารถส่งผลลัพธ์ไปต่อท้ายข้อมูลในไฟล์ที่มีอยู่แล้วได้โดยใช้เครื่องหมาย >> ซึ่งจะเขียนผลลัพธ์ไปต่อท้ายไฟล์ที่มีอยู่หรือสร้างไฟล์ขึ้นมาหากยังไม่มีในระบบ (ทำงานเหมือน >)
การเปลี่ยนทิศทางของอินพุต
เราสามารถเปลี่ยนทิศทางของอินพุตได้เช่นเดียวกันโดยใช้อักขระ < เช่น เปลี่ยนการรับข้อมูลจากคีย์บอร์ดเป็นมารับข้อมูลจากไฟล์แทน ตามตัวอย่างด้านล่างเป็นการสั่งให้โปรแกรม wc อ่านข้อมูลจากไฟล์ passwd
wc < /etc/passwd
และเรายังใช้อักขระ | เพื่อส่งผลลัพธ์จากคำสั่งหนึ่งไปให้อีกคำสั่งหนึ่งได้ด้วย ตัวอย่างเช่น การใช้คำสั่ง cat อ่านข้อมูลจากไฟล์ passwd ส่งต่อไปให้โปรแกรม wc
cat /etc/passwd | wc
นอกจากนี้ยังมีอักขระอื่นๆอีกทั้งที่เป็นอักขระเดี่ยวๆ อักขระหลายตัวประกอบกัน หรือเป็นโครงสร้างของอักขระ เช่น $ – ใช้เป็นอักขระเพื่อบอกว่าสิ่งที่ตามมาเป็นตัวแปร, (..), {..}, [..],’, “, $((…)) เป็นต้น
คำสั่งที่โปรแกรมเชลล์รู้จัก
อย่างที่ทราบมาแล้วโปรแกรมเชลล์รับคำสั่งเพื่อไปสั่งระบบปฏิบัติการอีกทีหนึ่ง คำสั่งที่โปรแกรมเชลล์รู้จักสามารถแบ่งออกได้เป็น
1. คำสั่งที่เป็นโปรแกรม (compiled appliction) คือไฟล์ที่ผ่านการคอมไพล์มาแล้วซึ่งโดยทั่วไปจะเก็บอยู่ที่ /usr/bin เช่น คำสั่ง ls, rm หรือเครื่องมืออย่าง vi, gzip เป็นต้น
2. คำสั่งที่เป็นคำสั่งภายในของโปรแกรมเชลล์เอง โดยคำสั่งเหล่านี้จะใช้แสดงผลเท่านั้น โดยจะใช้ได้กับหน้าจอคอนโซลของเชลล์เองหรือในเชลล์สคริปเท่านั้น บางคำสั่งก็มีชื่อเดียวกับคำสั่งที่เป็นโปรแกรมโดยผลลัพธ์ที่ได้อาจจะแตกต่างกันนิดหน่อย ตัวอย่างของคำสั่งที่เป็นคำสั่งภายในของโปรแกรมเชลล์เอง เช่น cd, pwd, echo, read, logout, printf, let และ ulimit เป็นต้น เราสามารถดูคำสั่งที่เป็นคำสั่งภายในของโปรแกรมเชลล์ทั้งหมดได้จาก man page ของโปรแกรมเชลล์ เช่น man bash หรือพิมพ์ help ในหน้าจอคอนโซลของเชลล์
3. สคริปไฟล์ของภาษาอื่นอย่างเช่น perl หรือ python
ตัวแปร (argument) ที่ใช้รับค่าหรือข้อมูลอินพุตในสคริป
เชลล์สคริปรองรับการส่งค่าผ่านตัวแปร (argument) เพื่อใช้งานในเชลล์สคริปโดยรองรับการส่งค่าทั้งที่เป็นตัวเลขและตัวอักษร เช่น
./script.sh /tmp (เป็นการเรียกใช้งาน script.sh และส่งข้อมูลหรือข้อความในไฟล์ tmp ให้กับเชลล์สคริป)
./script.sh 100 200 (เป็นการเรียกใช้งาน script.sh และส่งค่า 100 และ 200 ให้กับเชลล์สคริป)
โดยภายในเชลล์สคริปจะมีตัวแปรที่รับข้อมูลที่ส่งเข้ามา (argument) โดยตัวแปรเหล่านั้นจะขึ้นต้นด้วยตัวอักษร $ แล้วตามด้วยตัวเลขหรืออักขระพิเศษ ตัวอย่างด้านล่างจะเป็นตัวแปรและความหมาย
$0 = ชื่อเชลล์สคริป,
$1 = ตัวแปรตัวแรกในเชลล์สคริป
$2,$3 .. = ตัวแปรตัวถัดๆไปในเชลล์สคริป
$* = ตัวแปรทั้งหมด
$# = จำนวนของตัวแปร
จากตัวอย่างด้านล่างเป็นการส่งค่าให้กับ helloworld.sh และแสดงค่าต่างๆที่เกี่ยวกับตัวแปรออกมา
ภาพแสดงการส่งค่าให้เชลล์สคริป
การแทนที่ด้วยคำสั่ง
ในกรณีที่เราอาจต้องส่งผลลัพธ์จากคำสั่งหนึ่งเพื่อให้อีกคำสั่งหนึ่งเอาไปใช้งานต่อ เราสามารถทำได้โดยใช้ ‘ หรือ $( ) ครอบคำสั่งไว้ ในที่นี้แนะนำให้ใช้ $( ) เพราะสามารถรองรับการทำ nesting (ซ้อนวงเล็บกันหลายๆชั้นเป็นโครงสร้างที่ซับซ้อนขึ้น) ตัวอย่างเช่น
ls /lib/modules/’uname -r’/ หรือ
ls /lib/modules/$(uname -r)/
จากตัวอย่างคำสั่ง uname จะถูกดำเนินการก่อน โดยจะถูกทำในเชลล์หรือโปรเซสที่แยกต่างหากออกไปและเมื่อได้ผลลัพธ์แล้วคำสั่ง ls จึงจะถูกดำเนินการ
การใช้ตัวแปรแวดล้อม (environment variables) กับเชลล์สคริป
ในลินุกซ์จะมีตัวแปรของระบบทั้งที่ระบบปฏิบัติการสร้างขึ้นมา (system-defined variables) หรือตัวแปรที่ผู้ใช้งานสร้างขึ้นมา (user-defined variables) โปรแกรมเชลล์จะรู้จักและสามารถใช้งานตัวแปรของระบบได้อยู่แล้ว เราสามารถนำค่าที่อยู่ในตัวแปรมาใช้งานในเชลล์สคริปได้ เช่น ใช้เป็นข้อมูลอินพุต ใช้เพื่อการตรวจสอบ หรือใช้เพื่อควบคุมการทำงานของเชลล์สคริป ตัวอย่างของตัวแปรแวดล้อมที่รู้จักกันดี เช่น HOME, PATH และ HOST โดยเราอ้างอิงตัวแปรด้วยการใช้ $ นำหน้าตัวแปร เช่น $HOME, $PATH เป็นต้น ตัวอย่างเช่น
echo $PATH (แสดงค่าพาธที่เก็บอยู่ในตัวแปร PATH)
แต่ในการแก้ไขหรือกำหนดค่าของตัวแปร ไม่ต้องใช้ $ นำหน้าตัวแปร ตัวอย่างเช่น
MYCOLOR=blue (เป็นการกำหนดค่า blue ให้กับตัวแปร MYCOLOR)
เราสามารถดูตัวแปรทั้งหมดที่มีอยู่ได้โดยใช้คำสั่ง env, set, หรือ printenv
การกำหนดให้ตัวแปรที่สร้างขึ้นมาเป็นตัวแปรแวดล้อม
ตัวแปรที่เราสร้างขึ้นมาเพื่อใช้งานในเชลล์สคริปใดเชลล์สคริปหนึ่งจะไม่สามารถใช้งานได้กับเชลล์สคริปอื่นเพราะแต่ละเชลล์สคริปต่างก็ทำงานในโปรเซสของตัวเอง ต่างกับตัวแปรระบบ (environment variables) ซึ่งทุกโปรเซสจะรู้จักและเราจึงสามารถอ้างอิงมาใช้งานได้เลย ดังนั้นในกรณีที่เราต้องการให้ตัวแปรที่เราสร้างขึ้นมาสามารถใช้งานโดยโปรเซสอื่นได้ (เช่นเราเขียนเชลล์สคริปแบบที่มีการแยกการทำงานออกเป็นเชลล์สคริปย่อยๆ) เราจะต้องประกาศให้ตัวแปรของเราเป็นตัวแปรระบบเสียก่อนโดยใช้คำสั่ง export ดังตัวอย่าง
export VAR=value หรือ VAR=value ; export VAR
อย่างไรก็ตามการแก้ไขค่าของตัวแปรในแต่ละโปรเซสจะไม่กระทบกันเนื่องจากเป็นการสำเนาตัวแปรและค่าของตัวแปรไปใช้งานเท่านั้น ไม่ใช่เป็นการอ้างอิงมายังตำแหน่งเก็บข้อมูลในหน่วยความจำที่เดียวกัน ถ้าเราพิมพ์คำสั่ง export เฉยๆ จะแสดงตัวแปรที่ถูก export ไปทั้งหมด
การสร้างฟังก์ชั่นในเชลล์สคริป
ฟังก์ชั่นคือกลุ่มของคำสั่งเพื่อการดำเนินการอย่างหนึ่งอย่างใด
ฟังก์ชั่นจะมีประโยชน์ในกรณีที่ต้องมีการดำเนินการซ้ำๆโดยอาจจะใช้อินพุตที่แตกต่างกันไปในแต่ละครั้ง บางทีเรามักเรียกฟังก์ชั่นว่าซับรูทีน (subroutines) การใช้งานฟังก์ชั่นประกอบด้วย 2 ขั้นตอนคือ การประกาศฟังก์ชั่น และ การเรียกใช้งานฟังก์ชั่น ไวยากรณ์ของการประกาศฟังก์ชั่นจะมีหน้าตาตามตัวอย่างด้านล่าง โดยเราต้องกำหนดชื่อของ
ฟังก์ชั่น (funtion_name) และการประกาศฟังก์ชั่นจะต้องอยู่ส่วนบนสุดของเชลล์สคริปถัดจากบรรทัด #!/bin/bash
function_name () {
command…
}
ตัวอย่างการประกาศฟังก์ชั่นชื่อ display เพื่อแสดงข้อความ
display () {
echo “This is a sample function”
}
ตัวอย่างด้านล่างเป็นการประกาศและเรียกใช้ฟังก์ชั่นพร้อมกับป้อนค่าให้ฟังก์ชั่น
การใช้โครงสร้าง if
จุดประสงค์ในการสร้างเชลล์สคริปขึ้นมาก็เพื่อช่วยให้งานเราง่ายขึ้นโดยเฉพาะงานที่ซับซ้อนซึ่งคงหลีกเลี่ยงไม่ได้ที่จะต้องมีส่วนที่เป็นเงื่อนไขในการทำงาน เช่น ถ้าไม่เป็นอย่างนี้ ให้ทำอย่างนั้น เป็นต้น เราสามารถกำหนดเงื่อนไขในการทำงานทำนองนี้ได้โดยการใช้ if
การใช้ if เป็นการกำหนดเงื่อนไขในการดำเนินการโดยจะขึ้นอยู่กับการประเมินค่าตามเงื่อนไขที่กำหนดว่าเป็นจริงหรือเป็นเท็จ เช่น การเปรียบเทียบตัวเลขหรือตัวอักษร การตรวจสอบค่าที่ส่งกลับมาจากคำสั่งว่าสำเร็จหรือไม่สำเร็จ การมีอยู่ของไฟล์ หรือสิทธิในการใช้ไฟล์ เป็นต้น รูปแบบของ if สามารถแสดงในแบบสั้นๆได้ดังนี้
if [เงื่อนไขที่ต้องการให้ตรวจสอบ]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
ซึ่งความหมายคือถ้าเงื่อนไขเป็นจริงก็ให้ทำตามคำสั่งที่กำหนดสังเกตุว่าถ้าเราเขียนทั้งหมดอยู่ในบรรทัดเดียวกันจะต้องคั่นแต่ละส่วนด้วยอักขระ ; และการเขียนแบบนี้รองรับคำสั่งเพียงคำสั่งเดียว ถ้าคำสั่งมีมากกว่าหนึ่งคำสั่งรูปแบบการเขียนจะเป็นตามตัวอย่างด้านล่าง
if [เงื่อนไขที่ต้องการให้ตรวจสอบ]
then
คำสั่งที่ต้องการให้ดำเนินการ
else
คำสั่งที่ต้องการให้ดำเนินการ
fi
ซึ่งความหมายคือถ้าเงื่อนไขเป็นจริงก็ให้ทำคำสั่งตามที่กำหนด แต่ถ้าไม่ตรงตามเงื่อนไข (else) ก็ให้ทำตามคำสั่งอีกชุดหนึ่งที่จัดเตรียมไว้ เราสามารถนำ if มาซ้อนกันในกรณีที่ต้องการตรวจสอบหลายเงื่อนไข เช่น
if [เงื่อนไขที่ต้องการให้ตรวจสอบ]
then
คำสั่งที่ต้องการให้ดำเนินการ
else
if [เงื่อนไขที่ต้องการให้ตรวจสอบ]
then
คำสั่งที่ต้องการให้ดำเนินการ
else
คำสั่งที่ต้องการให้ดำเนินการ
fi
fi
หรือในรูปแบบ
if [เงื่อนไขที่ต้องการให้ตรวจสอบ]
then
คำสั่งที่ต้องการให้ดำเนินการ
elif [เงื่อนไขที่ต้องการให้ตรวจสอบ]
then
คำสั่งที่ต้องการให้ดำเนินการ
else
คำสั่งที่ต้องการให้ดำเนินการ
fi
ตัวอย่างการใช้ if เพื่อตรวจสอบว่ามีไฟล์ /etc/passwd หรือไม่ ซึ่งถ้าพบไฟล์ให้แสดงข้อความ /etc/passwd exists
if [ -f /etc/passwd ](สังเกตุว่ามีการใช้ [ ] คลุมเพื่อบอกว่าส่วนนี้คือเงื่อนไข)
then
echo “/etc/passwd exists.”
fi
จากตัวอย่างด้านบนเป็นการทดสอบเงื่อนไขของไฟล์โดยการใช้ -f
ตัวอย่างการทดสอบเงื่อนไขของไฟล์ในเรื่องอื่นๆ เช่น
-e <ชื่อไฟล์> : เป็นการตรวจสอบว่ามีไฟล์นี้หรือไม่
-d <ชื่อไฟล์> : เป็นการตรวจสอบว่าเป็นไฟล์หรือไดเร็คทอรี่
-f <ชื่อไฟล์> : เป็นการตรวจสอบว่าเป็นไฟล์จริงๆ ไม่ใช่ลิงค์หรือโหนดที่แทนตัวอุปกรณ์หรือไดเร็คทอรี่
-s <ชื่อไฟล์> : เป็นการตรวจสอบขนาดของไฟล์ว่าไม่เป็น 0 (ไม่ใช่ไฟล์เปล่า)
-g <ชื่อไฟล์> : เป็นการตรวจสอบว่ามีการกำหนดค่า sgid ให้กับไฟล์แล้ว
-u <ชื่อไฟล์> : เป็นการตรวจสอบว่ามีการกำหนดค่า suid ให้กับไฟล์แล้ว
-r <ชื่อไฟล์> : เป็นการตรวจสอบว่าไฟล์มีการอนุญาติให้อ่าน (readable)
-w <ชื่อไฟล์> : เป็นการตรวจสอบว่าไฟล์มีการอนุญาติให้เขียน (writable)
-x <ชื่อไฟล์> : เป็นการตรวจสอบว่าไฟล์มีการอนุญาติให้ execute (executable)
เราสามารถดูรายการทดสอบเงื่อนไขของไฟล์ทั้งหมดที่มีให้ใช้งานได้โดยใช้คำสั่ง man 1 test ในหน้าต่างเทอร์มินอล
การใช้ตัวดำเนินการเปรียบเทียบ
การใช้ตัวดำเนินการเปรียบเทียบเป็นการเปรียบเทียบของสองสิ่งว่าเป็นจริงหรือเป็นเท็จโดยใช้ตัวดำเนินการเปรียบเทียบดังนี้
ตัวดำเนินการเปรียนเทียบทางตรรกะ
&& เป็นตัวดำเนินการ AND หมายถึงดำเนินการตามคำสั่งเมื่อค่าที่นำมาเปรียบเทียบกันเป็นจริงทั้งคู่
|| เป็นตัวดำเนินการ OR หมายถึงดำเนินการตามคำสั่งเมื่อค่าที่นำมาเปรียบเทียบกันค่าใดค่าหนึ่งเป็นจริง
! เป็นตัวดำเนินการ NOT หมายถึงกลับค่าจากจริงเป็นเท็จหรือจากเท็จเป็นจริง
ตัวอย่างการใช้งาน เช่น
if [A && B]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
if [A || B]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
if !A; then คำสั่งที่ต้องการให้ดำเนินการ; fi
ตัวดำเนินการเปรียบเทียบสำหรับตัวเลข
-eq (equal to) ค่าที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่าเท่ากับตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
-ne (not equal to) ค่าที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่าไม่เท่ากับตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
-gt (greater than) ค่าที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่ามากกว่าตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
-lt (less than) ที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่าน้อยกว่าตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
-ge (greater than or equal to) ค่าที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่ามากกว่าหรือเท่ากับตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
-le (less than or equal to) ค่าที่ได้จะเป็นจริงหากตัวเลขที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่าน้อยกว่าหรือเท่ากับตัวเลขที่อยู่ทางด้านขวาของตัวดำเนินการ
ตัวอย่างการใช้งาน เช่น
if [$number1 -gt $number2]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
ตัวดำเนินการทางคณิตศาสตร์
เราสามารถใช้ตัวดำเนินการทางคณิตศาสตร์ได้ใน 3 รูปแบบคือ
1. ใช้คำสั่ง expr เช่น expr 8 + 8 ตัวอย่างเช่น
echo $(expr 8 + 8)
2.ใช้ $((…)) เช่น $((8 + 8))ตัวอย่างเช่น
echo $((8 + 8)) หรือ x=$((8 + 8)); echo $x
3. ใช้คำสั่ง let เช่น let x=(8 + 8) ตัวอย่างเช่น
let x=( 8 + 8 ); echo $x
การจัดการข้อความ (String Manipulation)
การเอาข้อความมาใช้งานในเชลล์สคริปเราจะต้องเอาข้อความมาใส่ในตัวแปรเสียก่อน จากนั้นจึงจะนำมาใช้งานในด้านต่างๆ การจัดการข้อความในเบื้องต้นสามารถทำได้ดังนี้
string1 == string2 ค่าที่ได้จะเป็นจริงหากข้อความที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่าเท่ากับข้อความที่อยู่ทางด้านขวาของตัวดำเนินการ ตัวอย่างการใช้งาน
if [string1 == string2]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
string1 > string2 ค่าที่ได้จะเป็นจริงหากข้อความที่อยู่ทางด้านซ้ายของตัวดำเนินการมีค่ามากกว่าข้อความที่อยู่ทางด้านขวาของตัวดำเนินการ ตัวอย่างการใช้งาน
if [string1 > string2]; then คำสั่งที่ต้องการให้ดำเนินการ; fi
${#string1} เป็นการอ่านค่าความยาวของข้อความ ตัวอย่างการใช้งาน
myLen1=${#string1} คืออ่านค่าความยาวของข้อความไปเก็บที่ตัวแปร myLen1
${string:0:n} เป็นการอ่านเฉพาะบางส่วนของข้อความตามที่กำหนด โดยเริ่มต้นจากตัวอักษรตามตำแหน่งที่กำหนด และนับไปอีกตามจำนวนตัวอักษรที่กำหนดไว้ ตัวอย่างการใช้งาน
myPass=${string:0:5} เป็นอ่านข้อความ 5 ตัวอักษรแรกของข้อความ
${string#*.} เป็นการอานข้อความทั้งหมดที่อยู่หลังจุด (.)
การใช้โครงสร้าง case
การใช้โครงสร้าง case เป็นอีกวิธีหนึ่งในการกำหนดเงื่อนไขในการทำงานตามแต่ละคำสั่งโดยเฉพาะในกรณีที่มีการกำหนดคำสั่งที่แตกต่างกันไปจำนวนมากตามผลลัพธ์ที่หลากหลาย ซึ่งการใช้ if ซ้อนๆกันหลายชั้นอาจเสี่ยงต่อการเขียนผิดพลาด และทำความเข้าใจยากในภายหลัง รูปแบบการใช้ case มีดังนี้
case [เงื่อนไขที่ต้องการให้ตรวจสอบ] in
ค่าที่ตรงกัน1) คำสั่งที่ต้องการให้ดำเนินการ;;
ค่าที่ตรงกัน2) คำสั่งที่ต้องการให้ดำเนินการ;;
ค่าที่ตรงกัน3) คำสั่งที่ต้องการให้ดำเนินการ;;
ค่าที่ตรงกัน4) คำสั่งที่ต้องการให้ดำเนินการ;;
ค่าอื่นๆ* ) คำสั่งที่ต้องการให้ดำเนินการ;;
esac
การใช้โครงสร้างการวนซ้ำ (Loop)
โครงสร้างการวนซ้ำช่วยให้เราสามารถสั่งให้เชล์ทำคำสั่งเดิมซ้ำๆหลายครั้งๆได้ตามเงื่อนไขที่กำหนด
การใช้ for
การใช้ for จะเป็นการดำเนินการตามรายการค่าที่กำหนด โดยมีรูปแบบการใช้งานดังนี้
for ตัวแปร in รายการค่าที่กำหนด
do
คำสั่งที่ต้องการให้ดำเนินการ
done
การใช้ while
การใช้ while จะเป็นการดำเนินการเมื่อเงื่อนไขเป็นจริง โดยมีรูปแบบการใช้งานดังนี้ สังเกตุว่าหากเงื่อนไขไม่เป็นจริงตั้งแต่แรกจะไม่เกิดการทำตามคำสั่งในลูปเลย
while [เงื่อนไขที่ต้องการให้ตรวจสอบ]
do
คำสั่งที่ต้องการให้ดำเนินการ
done
ตัวอย่างการใช้งานเช่น
การใช้ until
การใช้ until จะเป็นการดำเนินการเมื่อเงื่อนไขเป็นเท็จ โดยมีรูปแบบการใช้งานดังนี้ สังเกตุว่าหากเงื่อนไขไม่เป็นเท็จตั้งแต่แรกจะไม่เกิดการทำตามคำสั่งในลูปเลย
until [เงื่อนไขที่ต้องการให้ตรวจสอบ]
do
คำสั่งที่ต้องการให้ดำเนินการ
done
การตรวจหาสาเหตุของข้อผิดพลาด (Debugging)
ในการทำงานกับเชลล์สคริปเราอาจจะพบกับข้อผิดพลาด (error) เช่น ข้อผิดพลาดที่เกิดจากตัวเชลล์สคริปเองอย่างเช่นเขียนคำสั่งผิด หรือ ข้อผิดพลาดจากสิ่งแวดล้อม เช่น ไม่มีไฟล์ที่ต้องการ หรือ ไม่ได้รับอนุญาติให้ทำตามทีต้องการ ข้อผิดพลาดเหล่านี้มักจะแสดงเป็นรหัสข้อผิดพลาดหรือมีข้อความบอกข้อผิดพลาด แต่ส่วนมากจะทำให้สับสนมากกว่าเข้าใจว่าสาเหตุของข้อผิดพลาดจริงๆคืออะไร ดังนั้นการตรวจหาสาเหตุของข้อผิดพลาด (debugging) จะช่วยเราในการแก้ไขปัญหาต่างๆเหล่านี้
การรันเชลล์สคริปในโหมดแก้ไขปัญหา (debug mode)
เราสามารถรันสคริปใน debug mode โดยใช้คำสั่ง bash –x ./script_file หรือครอบเฉพาะส่วนของโปรแกรมที่ต้องการ debug ด้วยคำสั่ง set -x และ set +x เช่น
คำสั่ง
คำสั่ง
set -x # turns on debugging
คำสั่ง
คำสั่ง
คำสั่ง
set +x # turns off debugging
คำสั่ง
คำสั่ง
คำสั่ง
ใน debug mode ระบบจะแสดงคำสั่งที่เราเขียนไว้ก่อนดำเนินการโดยจะนำหน้าด้วยเครื่องหมาย + ตัวอย่างการใช้งาน
การเปลี่ยนทิศทางการแสดงข้อผิดพลาด
ในระบบปฏิบัติการแบบ UNIX/Linux การแสดงผลของ file stream ประกอบด้วย
stdin (standard input) : สำหรับโปรแกรมที่ทำงานโดยใช้ command line ค่าตั้งต้นจะหมายถึง คีย์บอร์ดหรือหน้าจอคอนโซลมีค่า file descriptor เป็น 0
stdout (standard output) : สำหรับโปรแกรมที่ทำงานโดยใช้ command line ค่าตั้งต้นจะหมายถึงหน้าจอคอนโซลมีค่า file descriptor เป็น 1
stderr (standard error) : ค่าตั้งต้นจะหมายถึงการแสดงหรือบันทึกข้อความ error มีค่า file descriptor เป็น 2
เราสามารถเปลี่บนทิศทางของ stdout และ stderr ไปบันทึกยังไฟล์ได้เพื่อให้สามารถตรวจสอบได้ภายหลัง ตัวอย่างการใช้งาน เช่น
programname 2> error.log ซึ่ง 2> error.log เป็นการกำหนดให้บันทึกข้อผิดพลาดลงในไฟล์ error.log โดย 2 หมายถึง file descriptor 2 นั่นเอง
command 2> error.log ซึ่ง 2> error.log เป็นการกำหนดให้บันทึกข้อผิดพลาดลงในไฟล์ error.log โดย 2 หมายถึง file descriptor 2 นั่นเอง
command &>file ซึ่ง &> error.log เป็นการกำหนดให้บันทึกข้อผิดพลาดและผลลัพธ์ลงในไฟล์ file โดย & หมายถึง file descriptor 1และ2 นั่นเอง
command > file-name 2>&1 เป็นการกำหนดให้บันทึกข้อผิดพลาดและผลลัพธ์ลงในไฟล์ file เช่นเดียวกัน
การสร้างไฟล์และไดเร็คทอรี่ชั่วคราว
ในกรณีที่เราต้องการสร้างไฟล์และไดเร็กทอรี่ชั่วคราว (temporary file/directory) ซึ่งไฟล์และไดเร็กทอรี่ดังกล่าวควรจะหายไปหลังจากเลิกใช้งาน ถึงแม้ว่าเราจะสามารถใช้คำสั่ง touch ในการสร้างไฟล์ได้แต่การใช้ชื่อไฟล์เดิมๆทุกครั้งอาจจะเป็นช่องทางให้ผู้ไม่หวังดีสามารถศึกษาและเข้าถึงข้อมูลของเราได้ เราสามารถสร้างไฟล์ที่มีชื่อไฟล์แบบสุ่มได้โดยใช้โปรแกรม mktemp ซึ่งจะสุ่มตัวอักษรและเฉพาะโปรแกรมของเราเท่านั้นที่เข้าใจและเอาไปใช้งานได้ รูปแบบการใช้งานคือ
TEMP=$(mktemp /tmp/tempfile.XXXXXXXX) เป็นคำสั่งที่ใช้สร้างไฟล์ชั่วคราว
TEMPDIR=$(mktemp -d /tmp/tempdir.XXXXXXXX) เป็นคำสั่งที่ใช้สร้างไดเร็คทอรี่ชั่วคราว
การทิ้งผลลัพธ์ลงใน /dev/null
ในบางครั้งคำสั่งที่เราสั่งไปจะแสดงผลลัพธ์มากมายมหาศาลบนหน้าจอคอนโซลเราสามารถสั่งให้ strout ส่งผลลัพธ์ไปยังไฟล์ /dev/null ซึ่งเป็นเหมือนอุปกรณ์ที่ไม่มีตัวตน ตัวอย่างการใช้งาน เช่น
$ ls -lR /tmp > /dev/null เป็นการเปลี่ยนทิศทางของผลลัพธ์ไปยัง /dev/null แต่ข้อผิดพลาดยังคงแสดงบนหน้าจอ
$ ls -lR /tmp >& /dev/null เป็นการเปลี่ยนทิศทางของผลลัพธ์และข้อผิดพลาดไปยัง /dev/null
การสร้างค่าสุ่ม (Random Numbers and Data)
การสร้างค่าสุ่มทั้งแบบสุ่มตัวเลขหรือสุ่มตัวอักษรมีประโยชน์มากสำหรับงานที่เกี่ยวกับเรื่องความปลอดภัย เช่น ใช้ในการล้างข้อมูลโดยเขียนค่าสุ่มทับลงไป หรือการสร้างข้อมูลที่ไม่มีความหมายสำหรับการทดสอบ การสร้างค่าสุ่มทำโดยใช้ตัวแปรระบบ $RANDOM ซึ่งจะเป็นการใช้งานค่าสุ่มจากลินุกซ์เคอร์เนล หรือจากโปรแกรม OpenSSL ซึ่งเป็นการสุ่มค่าโดยใช้อัลกอริธึม FIPS140
ภาพแสดงตัวอย่างเลขสุ่ม
วิธีการที่เคอร์เนลสร้างค่าสุ่ม
เครื่องคอมพิวเตอร์ที่ออกแบบมาเฉพาะทางจะมีฮาร์ดแวร์ที่คอยจับค่าต่างๆรอบตัว เช่น ค่าการเปลี่ยนแปลงของกระแสไฟฟ้า ค่าการเปลี่ยนแปลงของอุณหภูมิหรือแสง และจะบันทึกค่าต่างๆเหล่านั้นไว้ จากนั้นจึงแปลงค่าที่ตรวจจับได้นี้มาเป็นตัวเลขสุ่ม แต่คอมพิวเตอร์ทั่วไปที่ไม่มีฮาร์ดแวร์แบบนี้ก็จะใช้ค่าของเหตุการณ์ต่างๆในตัวเองตั้งแต่เริ่มเปิดเครื่องขึ้นมา เช่น การขยับเมาส์ หรือ การสื่อสารผ่านเครือข่าย โดยจะเก็บค่าไว้ จากนั้นจึงใช้ค่าดังกล่าวมาสร้างเป็นตัวเลขสุ่ม
ลินุกซ์เคอร์เนลมีไฟล์ที่ใช้ในการสร้างค่าสุ่ม 2 ไฟล์คือ /dev/random และ /dev/urandom โดย /dev/random จะเป็นค่าสุ่มที่มีคุณภาพสูงแต่ทำงานช้า เหมาะกับการสุ่มค่าสำหรับ one-time pad หรือ การสร้างคีย์ ส่วน /dev/urandom จะทำงานได้เร็วกว่าและมีคุณภาพเพียงพอสำหรับการเข้ารหัสทั่วไป ในกรณีที่ไม่มีข้อมูลเก็บไว้จะไม่สามารถสร้างเลขสุ่มได้ จนกว่าจะมีข้อมูลเหตุการณ์เข้ามา