Note09 ----- ตอนที่ 1 ปฏิบัติการจาก Prac09.txt ----------------------------- 1. ใช้คำสั่ง help เพื่อดูรายละเอียดของคำสั่ง alias และ unalias ของ bash -- คำสั่งทั้งสองใช้ทำอะไร? -- คำอธิบายที่ได้จาก help เพียงพอต่อการทำความเข้าใจหรือไม่? หากไม่เข้าใจควรจะต้องทำอย่างไร? ความหมาย -------- alias แปลว่า ฉายา, นามแฝง, หรือ สมนามในสาขาคอมพิวเตอร์และสารสนเทศ ใน bash, alias เป็นการตั้งชื่อย่อให้กับคำสั่งเพื่อลดภาระในการพิมพ์ เช่น $ alias lm='ls -l | more' เป็นการกำหนดให้ lm เป็นชื่อย่อของคำสั่ง ls -l | more เมื่อกำหนดแล้ว ต่อไปเมื่อป้อนชื่อ lm ระบบจะเปลี่ยนแทนด้วย ls -l | more การทำงานลักษณะนี้ช่วยให้ผู้ใช้สามารถตั้งชื่อย่อให้กับการทำงานของ pipeline ที่ซับซ้อนได้ และหากนำไปกำหนดไว้ในแฟ้ม .bash_profile จะสามารถใช้งานชื่อย่อนี้ได้ทุกครั้งที่มีการ login สำหรับ alias ที่นิยมใช้งานมากตัวหนึ่งคือ $ alias rm='rm -i' ซึ่งทำให้คำสั่ง rm กลายเป็น rm -i หรือเป็นการลบแฟ้มแบบมีการโต้ตอบกับผู้ใช้ เช่น $ rm test.dat rm: remove regular file 'test.dat'? _ ผู้ใช้ต้องตอบ 'y' ระบบจึงจะทำการลบแฟ้มให้ หากกดแป้นอื่นจะไม่มีการลบแฟ้ม ซึ่งช่วยป้องกันการลบแฟ้มผิดของผู้เริ่มต้นใช้งานได้พอสมควร การยกเลิก alias ที่กำหนดไว้ ทำได้โดยใช้คำสั่ง unalias เข่นต้องการยกเลิกชื่อย่อ lm $ unalias lm 4. แฟ้ม test1.dat เป็น text file ที่มีเพียงบรรทัดเดียว ประกอบด้วยคำภาษาอังกฤษ 66 คำ แต่ละคำคั่นด้วย tab จงเขียน pipeline ที่ประกอบด้วยคำสั่ง tr และ sort เพื่อเรียงคำในแฟ้มตามพจนานุกรม และเขียนผลลัพธ์ลงในแฟ้ม test2.dat การวิเคราะห์งาน - ข้อจำกัดของ sort อยู่ที่การเรียงบรรทัดข้อความในแฟ้ม ตาม key ที่ผู้ใช้กำหนด หากไม่ระบุค่าโดยปริยายจะเป็นฟิลด์แรก - แฟ้ม input มีเพียงบรรทัดเดียว ไม่สามารถเรียงข้อมูลตามความต้องการได้ ต้องกระจายคำในบรรทัดนั้นออกเป็น 66 บรรทัด บรรทัดละคำ จึงจะสามารถเรียงข้อมูลในแต่ละบรรทัดด้วย sort ได้ - กระจายคำในบรรทัดเดียว 66 คำ แต่ละคำคั่นด้วย tab เป็น 66 บรรทัด บรรทัดละคำ ทำได้โดยการเปลี่ยน tab เป็น newline หรือหากไม่แน่ใจว่านอกจาก tab แล้ว จะมี white space อื่นเช่น ววรรค อยู่ด้วยหรือไม่ ควรต้องกำหนดเป็นการเปลี่ยนแทน white space ด้วย newline -- อักขระในกลุ่ม white space มีชื่อ class ตามมาตรฐาน POSIX เป็น [:space:] - การรวมบรรทัด 66 บรรทัดเป็นบรรทัดเดียว ทำได้โดยการเปลี่ยน newline กลับมาเป็น tab - การแยกและการรวมบรรทัดโดยวิธีการเปลี่ยนแทนอักขระ ทำได้โดยใช้คำสั่ง tr ขั้นตอนวิธี 1. ใช้คำสั่ง tr เปลี่ยน white space ('[:space:]') ที่พบทั้งหมดเป็น newline ('\n') ----> output มี 66 บรรทัด บรรทัดละคำ 2. sort ผลลัพธ์ที่ได้จากข้อ 1. 3. ใช้คำสั่ง tr เปลี่ยน newline ('\n') ที่พบทั้งหมดเป็น tab ('\t') ----> output มีบรรทัดเดียว 66 คำ ประยุกต์การทำงานด้วย pipeline $ tr '\t' '\n' < test.dat | sort | tr '\n' 't' > test2.dat ตอนที่ 2 คำสั่งภายในของเชลล์ (เพิ่มเติม) ------------------------------- 1. คำสั่ง let มีรูปแบบการใช้งานดังนี้ let arg [arg ...] ใช้สำหรับประมวลผลนิพจน์คณิตศาสตร์ เพื่อนำค่ามากำหนดให้กับตัวแปร ค่าที่ได้เป็นจำนวนเต็มที่มีพิสัยจำกัด สนับสนุนการใช้งานเครื่องหมายทางคณิตศาสตร์และเปรียบเทียบแบบเดียวกับที่ใช้ในภาษา C และมีลำดับความสำคัญแบบเดียวกัน มีการเตือนเฉพาะเมื่อตัวหารเป็น 0 แต่ไม่มีการเตือนหากผลลัพธ์ล้นที่เก็บ (overflow) สามารถกำหนดนิพจน์คณิตศาสตร์ได้หลายชุดต่อในคำสั่ง let เดียวกัน ดังนั้นนิพจน์คณิตศาสตร์แต่ละชุดจึง "ต้อง" ไม่มีวรรคเลย เช่น $ let x=45+56 y=7*8 คำสั่ง let จะทำการคำนวณค่านิพจน์ที่กำหนด และนำผลลัพธ์ที่ได้มากำหนดให้กับตัวแปร สำหรับเชลล์ในปัจจุบัน ไม่ค่อยนิยมใช้คำสั่ง let แล้ว แต่ใช้ ((...)) กำหนดค่าตัวแปร เช่น $ (( x = 45 + 7, y = 7 * 8)) ((...)) ให้อิสระมากกว่าสำหรับผู้ที่คุ้นเคยกับการมีวรรคระหว่างนิพจน์ นอกจากนี้แลัว ยังสามารถนำผลลัพธ์ของนิพจน์คณิตศาสตร์ที่คำนวณด้วย $((...)) เพื่อนำค่ามาใช้เป็นอาร์กิวเมนต์ของคำสั่ง เช่น $ a=$(((6-4)-2)) รายละเอียดนอกเหนือจากนี้ อ่านจาก Lect10.txt ของสัปดาห์นี้ 2. Command history bash เก็บบรรทัดคำสั่งที่ผู้ใช้แต่ละคนเคยใช้งานไว้ใน buffer (หน่วยความจำพักข้อมูล) และ/หรือ ในแฟ้ม ~/.bash_history ผู้ใช้เปลี่ยนเป็นแฟ้มที่ต้องการได้ โดยเปลี่ยนค่าในตัวแปร HISTFILE และเปลี่ยนจำนวนคำสั่งที่เก็บในแฟ้มได้ด้วยการเปลี่ยนค่าในตัวแปร HISTSIZE ซึ่งมีค่าโดยปริยายเป็น 500 การเรียกดูคำสั่งที่เก็บในแฟ้มทำได้โดยใช้คำสั่ง history หรือ cat ~/.bash_profile การเรียกคำสั่งย้อนหลัง ทำด้วยแป้นลูกศรขึ้น และลูกศรลง หรือใช้เครื่องหมายอัศเจรีย์ (exclamation mark, !) หรือเรียกกันอย่างง่ายว่าเครื่องหมายตกใจ โดยเริ่มมีใช้งานเป็นครั้งแรกใน C shell ในช่วงก่อนที่จะมีแป้นลูกศร เช่น $ !6 # ให้ทำงานตามคำสั่งหมายเลข 6 ใน history buffer $ !-3 # ให้ทำงานตามคำสั่งที่เคยสั่งงานย้อนหลังไปสามคำสั่ง $ !! # ให้ทำงานตามคำสั่งล่าสุดอีกครั้งหนึ่ง ในปัจจุบันแทบไม่มีใครรู้จักแล้ว เพราะสามารถกดแป้นขึ้นเพื่อเรียกคำสั่งที่เคยใช้งานมาก่อน และกดแป้น กับคำสั่งที่ต้องการ นอกจากนี้ $ !cat # ให้ทำงานตามบรรทัดคำสั่งที่ขึ้นต้นด้วยคำสั่ง cat ครั้งล่าสุด นอกจากนี้ยังสามารถกำหนดให้ค้นหาบรรทัดคำสั่งที่ขั้นต้นด้วยคำสั่งที่ระบุและเปลี่ยนแทนด้วยคำสั่งอื่น เช่น $ !?cat?:s/cat/vi # หาบรรทัดคำสั่งที่ขึ้นต้นด้วย cat และเปลี่ยนแทนคำสั่ง cat ด้วย vi ในปัจจุบันมีการใช้งานน้อย และผู้ใช้งานรุ่นใหม่แทบไม่รู้จักเรื่องนี้เลย เนื่องจากมีแป้นลูกศรที่ถึงแม้จะมีประสิทธิภาพน้อยกว่าแต่ก็ใช้งานได้ง่ายกว่ามาก 3. การติดตามการทำงานของโปรแกรม การสร้าง Bourne again shell ใหม่ โดยใช้ตัวเลือก -x ในรูปแบบ $ bash -x ชื่อแฟ้ม-script [รายการอาร์กิวเมนต์ของ-script ...] ช่วยให้สามารถคิดตาม (trace) การทำงานของคำสั่งทั้งหมดใน script ได้ในระดับหนึ่ง หากต้องการควบคุมให้ทำงานในช่วงที่ต้องการ กำหนดด้วย #!/bin/bash ... ... set -x # เริ่มติดตามการทำงาน ... ... ... ... คำสั่งในช่วงที่ต้องการติดตามการทำงาน ... ... ... set +x # ยกเลิกการติดตามการทำงาน ... ... bash -x ทำหน้าที่เป็น debugger อย่างง่าย มีขีดความสามารถจำกัด อาจดูยากและเข้าใจยากสำหรับผู้เริ่มต้น เหมาะสำหรับการทำความเข้าใจการทำงานของเชลล์ในการแทนค่าตัวแปร แทนคำสั่งด้วยผลลัพธ์ และการดำเนินการทางคณิตศาสตร์ 4. คำสั่ง set คำสั่ง set ของเชลล์มีตัวเลือกที่ครอบคลุมการทำงานหลายด้าน สำหรับการหาที่ผิดในโปรแกรม (debugging) มีตัวเลือกที่น่าสนใจสามแบบคือ set -e กำหนดให้ script เลิกทำงานทันที หากคำสั่งใดคำสั่งหนึ่งใน pipeline ทำงานไม่สำเร็จ (มี exit status ไม่เป็น 0) เพื่อป้องกันไม่ให้เกิดความผิดพลาดต่อเนื่องไปใน pipeline set -f กำหนดไม่ให้เชลล์ขยายอักขระพิเศษในชื่อแฟ้ม set -x พิมพ์คำสั่งที่มีการขยายแล้ว ก่อนจะทำงานตามคำสั่งนั้น หรือเป็นคำสั่งที่มีการแทนค่าตัวแปร และแทนคำสั่งด้วย ผลลัพธ์แล้ว โดยนำหน้าบรรทัดคำสั่งด้วยอักขระ หรือสายอักขระ ที่กำหนดไว้ในตัวแปร PS4 set -v แสดงบรรทัดคำสั่งที่อ่านมาจากแฟ้ม script โดยตรง (ไม่มีการขยาย) ก่อนจะทำงานตามบรรทัดคำสั่งนั้น 5. สัญลักษณ์ หรือ ข้อความ "พร้อม" รับคำสั่งของเชลล์ (Prompt) ของเชลล์มี 4 แบบคือ PS1 ใช้แสดงว่าเชลล์กำลังรอรับคำสั่ง ค่าโดยปริยายเป็น "\s-\v\$ " คือ -shell-version$ เช่น "-bash-4.3$ " PS2 ใช้แสดงว่าบรรทัดคำสั่งยังไม่จบ และเชลล์กำลังรดให้ผู้ใช้ดำเนินการต่อไป ผู้ใช้กดแป้น โดยที่คำสั่ง ในบรรทัดยังไม่จบ เช่น มีเครื่องหมายคำพูดเปิด แต่ยังไม่มีเครื่องหมายคำพูดปิด เชลล์จะแสดง PS2 เพื่อให้ ผู้ใช้ป้อนคำสั่งที่เหลือให้จบ ค่าโดยปริยายเป็นเครื่องหมาย ">" PS3 ใช้แสดงว่าคำสั่ง select กำลังรอรับข้อมูล หากไม่กำหนดมีค่าโดยปริยายเป็น #? หมายถึงให้ป้อนหมายเลข (#) ของตัวเลือก PS4 ใช้นำหน้าบรรทัดคำสั่งเมื่อมีการทำ tracing (xtrace) ค่าโดยปริยายเป็น + หรือมีความหมายสั้นๆ จำได้ง่ายในภาษาอังกฤษเป็น PS1 - waiting for command PS2 - continuation prompt PS3 - 'select command' is waiting for input PS4 - debugging trace line prefix 5. การเขียนโปรแกรมด้วยภาษาสำหรับเขียนโปรแกรม เช่น ภาษา C, C++ และ Java เป็นต้น ผู้เขียนต้องเลือกใช้ algorithm ที่มีประสิทธิภาพสูงและเหมาะสมกับงานที่ต้องทำ สำหรับ shell script เนื่องจากการดำเนินการเป็นการเรียกใช้โปรแกรมอรรถประโยชน์ (Utility) ที่มีอยู่แล้ว ประสิทธิภาพจึงเป็นไปตามโปรแกรมอรรถประโยชน์เหล่านั้น ไม่สามารถเปลี่ยนแปลงได้ แต่ไม่ได้หมายความว่าจะไม่พิจารณาเรื่องประสิทธิภาพเสียทีเดียว อาศัยความรู้เรืองระบบปฏิบัติการ ผู้เขียนโปรแกรมสามารถหลีกเลี่ยงการทำงานที่มีประสิทธิภาพต่ำได้ เช่น $ cat /tmp/myfile | egrep "mystring" บรรทัดคำสั่งนี้ ระบบปฏิบัติการต้องสร้างโพรเซสสำหรับคำสั่ง cat และสร้างโพรเซสสำหรับคำสั่ง egrep จากนั้นจึงทำการสร้าง pipe เชื่อมโพรเซสทั้งสองเข้าด้วยกัน เมื่อเทียบกับบรรทัดคำสั่ง $ egrep "mystring" /tmp/myfile ซึ่งทำงานได้เช่นเดียวกัน แต่มีการสร้างโพรเซสสำหรับคำสั่ง egrep เพียงโพรเซสเดียว ทำให้ทำงานได้เร็วกว่ามาก จุดอ่อนทำนองนี้จะพบเห็นได้ทั่วไปใน shell script จำนวนมาก อาจเป็นเพราะเรียนรู้มาจากตัวอย่างทำนอง ทำให้ติดเป็นนิสัยในการทำงาน เมื่อเข้าใจผลลัพธ์ที่เกิดขึ้นแล้ว ขอให้เขียนโปรแกรมด้วยความระมัดระวัง หลีกเลี่ยงการทำงานที่ไม่จำเป็น 6. ประเด็นสำคัญสำหรับผู้ใช้ตัวแปรคือต้องแยกแยะระหว่าง "ชื่อ" ตัวแปร (name) และ "ค่า" ของตัวแปร (value) นั้น ตัวแปรของเชลล์ VAR1 เป็นชื่อตัวแปร และ $VAR1 ใช้อ้างอิงถึงค่าของตัวแปร $ VAR1=64 $ echo VAR1 VAR1 $ echo $VAR1 64 การใช้เฉพาะชื่อตัวแปรโดยไม่ต้องนำด้วย $ มีเฉพาะในการประกาศ, กำหนดค่า (ทั้งกำหนดโดยตรงด้วย = และคำสั่ง read) และเมื่อใช้ในคำสั่ง unset, export, แลเมื่อใช้ในการคำนวณทางคณิตศาสตร์ด้วย $((...)), - arithmetic expansion และใช้ในส่วนหัวของ for loop ก. คำสั่งที่มีการใช้ตัวแปรโดยตรง ได้แก่ การประกาศ (คำสั่ง declare), การกำหนดค่า (ทั้งกำหนดโดยตรงด้วย = และคำสั่ง read) และการยกเลิก (คำสั่ง unset) ข. ใช้ในการคำนวณทางคณิตศาสตร์ด้วย $((...)) - จะนำหน้าตัวแปรด้วย $ หรือไม่ก็ได้ ค. ใช้ในส่วนหัวของ for loop เช่น for i in 1 2 3 4 5; # กำหนดค่าของตัวแปร i สำหรับการทำซ้ำแต่ละรอบ do echo $i # อ้างถึงค่าของตัวแปร i -- ต้องนำด้วย $ done การอ้างถึงค่าของตัวแปรที่อยู่ในเครื่องหมายคำพูด ("...") ไม่มีผลต่อการแทนค่าของตัวแปร การกำกับด้วยเครื่องหมายคำพูดแบบนี้ เรียกว่า partial quoting ในขณะที่เครื่องหมายคำพูดเดี่ยว ('...') ทำให้ $ตัวแปร กลายเป็นอักขระธรรมดา เรียกว่า full quoting โดยความเป็นจริงแล้ว "$ตัวแปร" เป็นรูปแบบอย่างง่ายของ "${ตัวแปร}" ซึ่งใช้วงเล็บปีกกากำกับขอบเขตของชื่อตัวแปร สำหรับใช้งานในกรณีที่ต้องการใช้ค่าของตัวแปรร่วมกับสายอักขระอื่น การยกเลิกค่าของตัวแปร ทำได้ดังนี้ VAR1= หรือเป็นการกำหนดให้ตัวแปร VAR1 มีค่าว่าง (null) - ค่าว่าง คือ ไม่มีค่าใดเลย (ไม่ใช่ 0) แต่หากนำตัวแปรที่มีค่าว่างไปใช้ในนิพจน์คณิตศาสตร์ ระบบจะกำหนดให้มีค่าเป็น 0 ทำให้หลายคนเข้าใจว่า ค่าว่างคือ 0 นอกจากรูปแบบนี้แล้ว ยังสามารถใช้คำสั่ง unset เพื่อกำหนดให้ตัวแปรมีค่าว่าง $ unset VAR1