Note04 - เก็บเล็กผสมน้อยจากปฏิบัติการครั้งที่ ๔ ๑. การทำงานของ Script และตัวแปร PATH ----------------------------------- แฟ้ม Script02 หากมีสิทธิการใช้งานเป็น -rwx------ 1 jira staff 1282 Jan 29 18:43 Script02 เมื่อสั่งให้ทำงาน และปรากฏผลดังนี้ $ Script02 Script02: command not found # ไม่พบแฟ้มคำสั่ง Script02 แสดงว่าผู้ใช้ไม่ได้กำหนด "ไดเรกทอรีปัจจุบัน" (.) ในเส้นทางการค้นหาแฟ้มโปรแกรม ในตัวแปร PATH ในแฟ้ม .bash_profile ในปฏิบัติการครั้งแรก เนื่องจากระบบปฏิบัติการ Unix จะค้นหาแฟ้มโปรแกรมเฉพาะที่กำหนดไว้ในตัวแปร PATH เท่านั้น ในกรณีนี้สามารถเรียกให้ทำงานได้โดยระบุเส้นทาง เช่น $ ./Script02 # แบบเดียวกับที่เคยเรียก a.out ด้วย ./a.out ๒. ชื่อ Script... --------------- การตั้งชื่อแฟ้มคำสั่งว่า script ไม่ใช่ทางเลือกที่ดี เพราะ script เป็นคำสั่งของ Unix สำหรับบันทึกการป้อนข้อมูลและการแสดงผลระหว่างการ login ของผู้ใช้ (script - make typescript of terminal session) ซึ่งจะได้ใช้งานต่อไป ถ้าจะใช้ต้องมีอักขระอื่นต่อท้าย เช่น 01, 02, ... ๓. ลำดับการทำงานของแฟ้มกำหนดลักษณะการทำงานของเชลล์ ----------------------------------------------- หลายคนไม่แน่ใจว่าจะกำหนดตัวแปร เช่น PATH, TERM, PS1 ไว้ในแฟ้มใด เนื่องจากแฟ้มกำหนดลักษณะการทำงานของเชลล์ มีหลายแบบ เช่น /etc/profile # profile โดยปริยายของผู้ใช้ทุกคน ~/.bash_profile ~/.bash_login ~/.profile ลำดับการทำงานของแฟ้มเหล่านี้เมื่อผู้ใช้ login ป็นไปตาม Algorithm ดังนี้ ขั้นตอนวิธี: การกำหนดคุณลักษณะของเชลล์ begin กำหนดลักษณะการทำงานของเชลล์ตามข้อมูลในแฟ้ม /etc/profile if มีแฟ้ม ~/.bash_profile then กำหนดลักษณะการทำงานตาม ~/.bash_profile else if มีแฟ้ม ~/.bash_login then กำหนดลักษณะการทำงานตาม ~/.bash_login else if มีแฟ้ม ~/.profile then กำหนดลักษณะการทำงานตาม ~/.profile end {if} end {if} end {if} end หมายเหตุ เนื่องจาก /etc/profile ทำงานทุกครั้งเมื่อผู้ใช้ login และหากผู้ใช้มีแฟ้ม ./bash_profile จะมีการทำงานตามแฟ้มนี้อีก ในกรณีที่มีการกำหนดค่าตัวแปรตัวเดียวกัน ค่าที่กำหนดภายหลังจะเป็นค่าจริงที่ใช้ในการทำงานของเชลล์ ๔. คำถามจาก Note03 ------------------ คำถาม: แฟ้มที่มีสิทธิการใช้งานเป็น - r-- --- --- เมื่อแก้ไขแฟ้มโดยใช้ vi จะได้รับคำเตือนว่า เป็นการเปลี่ยนข้อมูลของแฟ้มอ่านอย่างเดียว แต่ก็ยังสามารถบังคับให้ save ได้ โดยใช้ :wq! โปรแกรม vi ทำเช่นนี้ได้อย่างไร? คำตอบ: เพราะระบบยอมให้ทำ ในลักษณะเดียวกับการลบแฟ้มที่มีสิทธิการใช้งานเป็น - r-- --- --- ซึ่งระบบปฏิบัติการจะให้ผู้ใช้ยืนยันการลบ ในทำนองเดียวกัน vi ใช้ ! ในการบันทึกแฟ้มให้ ๕. เก็บตกจาก Lect04 ------------------ เมื่อผู้ใช้ป้อนบรรทัดคำสั่ง (command line) และกดแป้น ↩ เชลล์จะทำการแยกบรรทัดคำสั่งเป็นองค์ประกอบต่างๆ โดยถือว่าองค์ประกอบของบรรทัดคำสั่งแยกกันด้วย separator ได้แก่ วรรค (space), ย่อหน้า (tab), และขึ้นบรรทัด (newline) ซึ่งจะมีเพียงตัวเดียวหรือหลายตัวก็ได้ ระบบจะทำการแยกคำสั่งและอาร์กิวเมนต์ ในลักษณะเดียวกับที่ระบบส่งอร์กิวเมนต์ให้กับฟังก์ชัน main หรือ arguments to main ในภาษา C คือส่งจำนวนของอร์กิวเมนต์ และ array ของ pointers ไปยังสายอักขระของอาร์กิวเมนต์แต่ละตัว ตามรูปแบบของฟังก์ชันหลักดังนี้ int main ( int argc, char *argv[] ) ระบบปฏิบัติการสามารถส่ง command line argument ให้ฟังก์ชัน main() โดยส่งจำนวน และ array ของ pointer ไปยังสายอักขระของอาร์กิวเมนต์แต่ละตัว ในทำนองเดียวกัน บรรทัดคำสั่งของเชลล์ เช่น $echo∎∎Hello∎∎➤world.↩ # กำหนดให้สัญลักษณ์ ∎ แทน วรรค, สัญลักษณ์ ➤ แทน tab, และสัญลักษณ์ และ ↩ แทน newline ในบรรทัดคำสั่ง วรรค (space), ย่อหน้า (tab), และขึ้นบรรทัด (newline) ทำหน้าที่เป็นตัวแยกคำ (separator) ไม่ใช่ส่วนใดส่วนหนึ่งของบรรทัดคำสั่ง เมื่อทำการแยกคำ (word splitting) แล้วได้ผลเป็น คำสั่ง echo อาร์กิวเมนต์ลำดับที่ 1 hello อาร์กิวเมนต์ลำดับที่ 2 world. เมื่อคำสั่ง echo ทำงานและแสดงอาร์กิวเมนต์ทั้งสอง โดยคั้นระหว่างอาร์กิวเมนต์ด้วยวรรคหนึ่งวรรค $echo∎∎Hello∎∎➤world.↩ hello∎world. การกำกับ (Quoting) บรรทัดคำสั่ง มีจุดมุ่งหมายสองประการคิอ • ยับยั้งการทำ word splitting • ยับยั้งการขยายผล (expansion) ของอักขระพิเศษ (meta-character) การยับยั้งสามารถทำได้สามระดับ คือ • ยับยั้งอักขระพิเศษเพียงตัวเดียว ด้วย escape sequence • ยับยั้งอักขระพิเศษทั้งหมด ยกเว้น $, \, และ ` ด้วย double quote, และ • ยับยั้งทุกกรณี ด้วย single quote ในทางปฏิบัติต้องวิเคราะห์งานให้รู้ว่า ต้องการ "ยับยั้ง" อะไร? และต้องการ "ระดับ" ไหน? แล้วจึงเลือกวิธีการ quote ที่เหมาะสม ๖. คำสั่งภายในและคำสั่งภายนอก ------------------------ ▶ คำสั่งภายใน (internal command) คำสั่งภายใน เป็นคำสั่งของเชลล์ เป็นคำสั่งที่มีการทำงานรวดเร็ว เนื่องจากไม่ต้องมีการสร้างโพรเซสเพื่อทำงาน เช่น คำสั่งเปลี่ยนไดเรกทอรี (cd - change directory) เป็นต้น ผู้ใช้สามารถขอดูคำสั่งภายในทั้งหมดของ Bourne Again shell (bash) ได้ด้วยคำสั่ง help เช่น $ help GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) These shell commands are defined internally. Type `help' to see this list. ... ... ... job_spec [&] history [-c] [-d offset] [n] or his> (( expression )) if COMMANDS; then COMMANDS; [ elif > ... ... ... ... ... ... cd [-L|[-P [-e]] [-@]] [dir] readarray [-n count] [-O origin] [-s count] [-t] [-u> หากต้องการดูรายละเอียดของตำสั่งใด ใช้คำสั่ง help ตามด้วยคำสั่งที่ต้องการ เช่น $ help cd cd: cd [-L|[-P [-e]] [-@]] [dir] Change the shell working directory. ... ... ... คำสั่ง help สามารถใช้งานใน pipeline ได้ เช่น "help | more" หรือ "help cd | more" เป็นต้น หากเรียกใช้ online manual page จะไม่ปรากฏผลลัพธ์ใด เช่น $ man cd No manual entry for cd # ไม่มีคู่มือสำหรับคำสั่ง cd ▶ คำสั่งภายนอก (external command) คำสั่งภายนอก เป็นคำสั่งของระบบปฏิบัติการ จัดเก็บเป็นแฟ้มโปรแกรมไว้ในไดเรกทอรี เช่น /bin, /sbin, /usr/bin, และ /usr/sbin เป็นต้น เมื่อถูกเรียกใช้งานจะต้องอ่านแฟ้มคำสั่งมาทำการสร้างโพรเซส แลัวจึงทำงานได้ จึงทำงานได้ช้ากว่าคำสั่งภายใน สามารถตำแหน่งของโปรแกรมในระบบแฟ้มได้ด้วยคำสั่ง which เช่น $ which ls /bin/ls $ which passwd /usr/bin/passwd และสามารถอ่านรายละเอียดได้จาก online manual page เช่น "man ls" และ "man passwd" เป็นต้น การแยกว่าคำสั่งใดเป็นคำสั่งภายในหรือภายนอก ทำได้โดยใช้คำสั่ง type (คำสั่งภายใน) เช่น $ type cd cd is a shell builtin # cd เป็นคำสั่งที่สร้างไว้ในเชลล์ $ type cat cat is /bin/cat # cat คือแฟ้ม /bin/cat จากปฏิบัติการที่ผ่านมา คำสั่ง expr สำหรับประมวลผลนิพจน์คณิตศาสตร์เป็นคำสั่งภายนอก ทำงานได้ช้ากว่า $((...)) ที่เป็นคำสั่งภายใน ในปัจจุบนจึงนิยมใช้ $((...)) หรือ arithmetic expansion มากกว่า ๗. จิปาถะ - arithmetic expansion, command substitution, และ pipeline ▶ คำสั่ง cal - แสดงปฏิทิน มีรูปแบบการใช้งานอย่างง่ายหลายรูปแบบ $ cal # แสดงปฏิทินของเดือนปัจจุบัน $ cal -m <เดือน> # แสดงปฏิทินของเดือนที่กำหนด ในปีปัจจุบัน <เดือน> ใช้งานได้สองแบบคือ # หมายเลขเดือน, 1..12 หรือชื่อย่อของเดือน, Jan .. Dec $ cal <ปี ค.ศ.> # แสดงปฏิทินของปีที่กำหนด คำสั่ง cal ยังมีความสามารถอื่นอีกมากมาย สนใจอ่านได้จาก online manual page ▶ คำสั่ง date - แสดงวันเวลาของระบบ ผู้ใช้สามารถเรียกใช้เพื่อแสดงวันเวลาของระบบ ผู้ดูแลระบบ (root) สามารถตั้งวันเวลาของระบบได้ด้วย $ date Sun Jan 28 17:19:23 +07 2018 ▶ คำสั่ง cut - ตัด "ส่วน" ที่ระบุของแต่ละบรรทัดจากแฟ้มที่กำหนด "ส่วน" ของบรรทัดกำหนดด้วย ไบต์ (-b), อักขระ (-c), หรือ ฟิลด์ (-f) ซึ่งกำหนดตัวแยกฟิลด์ไว้เป็น tab หากใช้ตัวอื่นต้องกำหนดด้วย -d ตัวอย่างการใช้งาน # พิมพ์ปฏิทินของปี พ.ศ. 2561 (ใช้ arithmetic expansion คำนวณปี ค.ศ.) $ cal $((2561 - 543)) # ตัด ปี ค.ศ. มาจากผลลัพธ์ของ date ด้วย cut - ปี ค.ศ. เป็นอักขระตำแหน่งที่ 24 - 28 $ date | cut -c24-28 2018 # พิมพ์ปฏิทินของปีปัจจุบัน โดยนำผลลัพธ์ของ pipeline มาใช้กับคำสั่ง cal ด้วย command substitution $ cal $(date | cut -c24-28) # พิมพ์ปี พ.ศ. จากวันเวลาของระบบ โดยใช้ command substitution และ arithmetic expansion $ echo "This year is $(( $(date | cut -c24-28) + 543)) B.E." This year is 2561 B.E. # แสดงการตัดปี ค.ศ. ซึ่งเป็นฟิลด์ที่ 6 โดยกำหนด delimiter เป็นวรรค (default เป็น tab) $ date | cut -d' ' -f6 2018 ลองคิดดูว่าจะทดลองทำอะไรเพิ่มเติมเพื่อเพิ่มพูนประสบการณ์ และนำความรู้ไปประยุกต์ใช้งานจริงได้