ขอบคุณ http://www.thaitux.info/book/export/html/366
bash: แตะ Shell Script
ขออนุญาตเขียนแบบกองโจรนะครับ โดยมือใหม่ เพื่อมือใหม่ครับ
เอามาจาก tldp: BASH Programming – Introduction HOW-TO
ศึกษาเพิ่มเติมได้จาก tldp: Bash Guide for Beginners
และในเชิงลึก จาก tldp: Advanced Bash-Scripting Guide1.เกริ่น
เชลล์สคริปต์ พูดง่าย ๆ ก็คือการนำคำสั่งในเชลล์ของลินุกซ์มาเรียงต่อกันให้ทำงานตามที่เราต้องการ โดยเพิ่มโครงสร้างการวนรอบ และฟังก์ชั่นต่าง ๆ เติมเข้ามา เพื่อให้การทำงานได้ตามที่เราต้องการ ซึ่งจะเหมาะมากกับงานแบบ batch หรืองานแบบ schedule
ฉะนั้นการที่จะเขียนโค๊ดให้ได้ดี จึงต้องศึกษาจดจำคำสั่งต่าง ๆ ของเชลล์ให้ได้เท่าที่เราต้องการใช้งาน (จำหมดคงไม่ไหว)
คำสั่งต่าง ๆ สามารถดูได้ที่ gnu.org: Bash Reference Manualสำหรับเดเบียน หากต้องการใช้งาน bash แบบเต็มรูป (ไม่อั้นความสามารถ) อาจต้องปรับแต่งเล็กน้อย
เปลี่ยนให้เชลล์ของเราเป็น bash แทน sh ใช้คำสั่ง$ chsh -s /bin/bashสำหรับเอดิเตอร์ ถ้าใช้ vi ควรติดตั้ง vim-full และอย่าลืมแก้ไขไฟล์ vimrc ให้แสดงสีด้วย เพื่อให้ดูโค๊ดได้ง่ายขึ้น
$ sudo aptitude install vim-full $ vi ~/.vimrcsyntax on:wq2.เริ่มเขียน
2.1 สคริปต์ Hello World
สมมุติตั้งชื่อสคริปต์ว่า hello.sh
$ vi hello.sh#!/bin/bash echo Hello World:wqอย่าลืมเปลี่ยนสถานะเพื่อให้สคริปต์สามารถรันได้
$ chmod 755 hello.shเริ่มรัน
$ ./hello.sh Hello Worldเรียบร้อยแล้ว
บรรทัดแรก เรียกว่า hash-bang เป็นการบอกให้เชลล์รู้ว่า โค๊ดที่เราเขียนนี้จะถูกประมวลผลด้วยโปรแกรมอะไร ในที่นี้คือ
/bin/bash
บรรทัดที่สอง เป็นการสั่งให้พิมพ์Hello Worldออกทางจอภาพ
2.2 สคริปต์สำหรับสำรองข้อมูล
จากตัวอย่างข้างบน ผมเขียนอธิบายโดยละเอียดโดยใช้เอดิเตอร์ vi แต่เพื่อให้กระชับเข้า จะขอละเลยการใช้เอดิเตอร์ โดยจะเขียนเฉพาะโค๊ดอย่างเดียวครับ
#!/bin/bash tar -cvzf /tmp/my-backup.tgz /home/USER/บรรทัดที่สองให้เปลี่ยนคำว่า USER เป็นชื่อเรา
เป็นการสั่งให้ใช้คำสั่ง tar ทำการสำรองข้อมูลพร้อมบีบอัดข้อมูลในไดเรคทอรี่ของบ้านเราไปสู่ไฟล์ชื่อ/tmp/my-backup.tgz3. การเปลี่ยนทิศข้อมูล (Redirection)
ใช้สัญญลักษณ์
>ใสการเปลี่ยนทิศ3.1 ข้อมูลมาตรฐาน
ข้อมูลมาตรฐานในเชลล์จะมีอยู่ 4 ชนิด คือข้อมูลเข้า(stdin), ข้อมูลแสดงผล(stdout), ข้อมูลข้อผิดพลาด(stderr), และแฟ้มข้อมูล(file)
ในทางปฏิบัติ เราสามารถเปลี่ยนทิศทางของข้อมูลเหล่านี้ไปมาได้ โดยมีมาตรฐานคือ 1 จะหมายถึงข้อมูลแสดงผล(stdout) และ 2 จะหมายถึงข้อมูลความผิดพลาด(stderr)เช่น
3.2 ตัวอย่างเปลี่ยน stdout ไปเป็น file
$ ls -l > ls-l.txtจะเปลี่ยนการแสดงผลของคำสั่ง
ls -lไปเก็บไว้ที่ไฟล์ชื่อ ls-l.txt ดังนั้นคำสั่งตามตัวอย่างนี้จะไม่แสดงอะไรออกมาทางจอภาพ แต่จะเก็บไว้ที่ไฟล์แทน หากเราต้องการดูผล สามารถใช้คำสั่งแสดงผลของไฟล์ได้คือ$ cat ls-l.txt
3.3 ตัวอย่างเปลี่ยน stderr ไปเป็น file
$ grep da * 2> grep-errors.txtตัวอย่างนี้เป็นการค้นหาข้อความ
daในทุกไฟล์ (*) และหากเกิดข้อผิดพลาดขึ้น จะนำข้อความผิดพลาดไปเก็บไว้ที่ไฟล์ชื่อ grep-errors.txt
3.4 ตัวอย่างเปลี่ยน stdout ไปเป็น stderr
$ grep da * 1>&2เป็นการค้นหาข้อความ
daในทุกไฟล์ (*) โดยนำการแสดงผลไปใส่ไว้ใน stderr แทนการแสดงผลปกติ แต่ในกรณีนี้เราป้อนคำสั่งทางแป้นพิมพ์ stdout และ stderr คือจอภาพเหมือนกัน จึงไม่เห็นความแตกต่าง แต่หากคำสั่งนี้ไปอยู่ในสคริปต์ที่เรากำหนดให้ stderr เป็นไฟล์ error-log การแสดงผลก็จะถูกเปลี่ยนทิศไปตามนั้น
3.5 ตัวอย่างเปลี่ยน stderr ไปเป็น stdout
$ grep da * 2>&1เป็นการค้นหาข้อความ
daในทุกไฟล์ (*) โดยหากเกิดข้อผิดพลาดขึ้น จะแสดงผลข้อผิดพลาดออกมาทาง stdout ซึ่งในที่นี้คือจอภาพเหมือนกัน
3.6 ตัวอย่างเปลี่ยน stderr และ stdout ไปยัง file
$ rm -f $(find /home/USER -name core) &> /dev/nullคำสั่งนี้เป็นการค้นหาไฟล์ในไดเรคทอรี่
/home/USERที่มีชื่อว่า core (find /home/USER -name core)
เมื่อพบแล้วก็จัดการลบทิ้งโดยไม่เตือน (rm -f)
โดยโยกการแสดงผลทั้งหมด (ทั้ง stderr และ stdout – ใช้สัญญลักษณ์&>) ไปยังไฟล์ชื่อ/dev/nullซึ่งเป็นไฟล์พิเศษ หมายความว่ายกเลิกการแสดงผลทั้งหมด
(คำสั่งนี้ค่อนข้างอันตราย เพราะลบโดยไม่เตือน โปรดทดลองด้วยความระมัดระวังครับ)4. การส่งต่อผลลัพธ์ หรือ ไปป์ (Pipes)
4.1 ความหมาย
ไปป์เป็นการส่งต่อผลลัพธ์จากคำสั่งหนึ่งไปเป็นค่านำเข้าของอีกคำสั่งหนึ่ง
4.2 ตัวอย่างไปป์
$ ls -l | sed -e "s/[aeio]/u/g"ตัวอย่างนี้จะนำเอาผลลัพธ์ที่ได้จากคำสั่ง
ls -lส่งต่อไปให้คำสั่งsed -e "s/[aeio]/u/g"
ซึ่งจะแปลงการแสดงผลจากอักขระ a หรือ e หรือ i หรือ o ไปเป็นอักขระ u ทั้งหมดเราอาจเขียนคำสั่งเทียบเท่าได้ดังนี้
$ ls -l > temp.txt $ sed -e "s/[aeio]/u/g" temp.txt $ rm temp.txtจะเห็นว่าการทำไปป์ ลดขั้นตอนไปมาก คงเหลือเพียงบรรทัดเดียว
4.3 ตัวอย่างไปป์ที่สอง
$ ls -l | grep "\.txt$"ตัวอย่างนี้จะส่งผลลัพธ์จากคำสั่ง
ls -lต่อไปให้คำสั่งgrep "\.txt$"คือให้แสดงเฉพาะไฟล์ที่มีนามสกุลเป็น.txtเท่านั้น
มีค่าเท่ากับคำสั่ง ls แบบใส่พารามิเตอร์กรอง$ ls -l *.txtหมายเหตุ
รูปแบบ"\.txt$"เป็นรูปแบบของ Regular Expression ซึ่งใช้มากในเชลล์สคริปต์ มีความหมายว่า “ที่ต้องลงท้ายด้วย .txt”5. ตัวแปร (Variables)
ตัวแปรในเชลล์สคริปต์ ไม่มีชนิดข้อมูล คือเราสามารถใช้ตัวแปรแทนตัวเลขหรืออักขระใด ๆ ก็ได้
โดยในขั้นตอนกำหนดค่า ไม่ต้องใช้เครื่องหมายใด ๆ นำหน้า แต่ตอนอ้างถึง ต้องใช้เครื่องหมาย$นำหน้าตัวแปร
5.1 ตัวอย่างสคริปต์ Hello World แบบใช้ตัวแปร
#!/bin/bash STR="Hello World!" echo $STRให้ผลลัพธ์เหมือนตัวอย่างที่ 2.1
ข้อควรระวังคือ
- การกำหนดค่าให้ตัวแปร อย่าเว้นวรรคระหว่างตัวแปรกับเครื่องหมาย
=- หากลืมใส่เครื่องหมาย
$จะหมายถึงการแสดงผลข้อความว่าSTRเฉย ๆ
5.2 ตัวอย่างสคริปต์สำรองข้อมูลแบบใช้ตัวแปร
#!/bin/bash OF=/tmp/my-backup-$(date +%Y%m%d).tgz tar -cvzf $OF /home/USER/ให้ผลลัพธ์คล้ายตัวอย่าง 2.2 แต่เพิ่มการใช้ตัวแปรลอยในคำสั่ง
$(date +%Y%m%d)ซึ่งมีผลทำให้ชื่อไฟล์ข้อมูลสำรองมีวันที่ต่อท้ายชื่อด้วย
5.3 ตัวแปรท้องถิ่น
ตัวแปรในเชลล์สคริปต์ทุกตัว จะเป็นตัวแปรรวม (Global) คือทุก ๆ ส่วนของโปรแกรมจะเห็นเหมือนกันหมด
แต่ในกรณีที่เราต้องการให้เห็นเฉพาะในฟังก์ชั่นที่เราต้องการ เราสามารถกำหนดให้ตัวแปรเป็นตัวแปรท้องถิ่นได้ด้วยคำสั่งlocal
เช่น#!/bin/bash HELLO=Hello function hello { local HELLO=World echo $HELLO }echo $HELLO
hello
echo $HELLOสคริปต์นี้ตัวแปร
HELLOในโปรแกรมหลัก กับในฟังก์ชั่นจะเป็นตัวแปรคนละตัวกัน6. ประโยคเงื่อนไข
6.1 รูปแบบ
มีรูปแบบคือ
if [EXPRESSION]; then CODE IF 'EXPRESSION' IS TRUE. [elif [EXPRESSION-ELIF]; then CODE IF 'EXPRESSION-ELIF' IS TRUE.] [else CODE IF NOTHING IS TRUE.] fi
6.2 ตัวอย่าง
if ... then#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true fiโค๊ดนี้จะเป็นจริงเสมอ ดังนั้นข้อความ “
expression evaluated as true” จะถูกพิมพ์ออกมาเสมอ
6.3 ตัวอย่าง
if ... then ... else#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true else echo expression evaluated as false fiโค๊ดนี้จะเป็นจริงเสมอ ดังนั้นข้อความ “
expression evaluated as true” จะถูกพิมพ์ออกมาเสมอ
6.4 ตัวอย่างแบบใช้ตัวแปร
#!/bin/bash T1="foo" T2="bar" if [ "$T1" = "$T2" ]; then echo expression evaluated as true else echo expression evaluated as false fiตัวอย่างนี้จะเป็นเท็จเสมอ
สังเกตุการใช้ตัวแปรในการเปรียบเทียบ ควรให้ตัวแปรอยู่ในเครื่องหมายคำพูดเสมอ เพื่อป้องการการผิดพลาดจากการแทนค่าที่ซับซ้อน หรือการที่มีช่องว่างในค่าตัวแปร7.การวนรอบ โดยใช้คำสั่ง for, while และ until
คำสั่ง
forมีลักษณะคล้าย for ในภาษาไพธอน มีรูปแบบเป็นfor VAR in SCOPE; do COMMAND doneคำสั่ง
whileมีรูปแบบเป็นwhile [CONDITION]; do COMMAND doneถ้าเงื่อนไข CONDITION เป็นจริง ก็จะทำคำสั่ง COMMAND คำสั่ง
untilรูปแบบตรงกันข้ามกับ while โดยมีรูปแบบเป็นuntil [CONDITION]; do COMMAND doneคือจะทำคำสั่ง COMMAND จนกว่าเงื่อนไข CONDITION จะเป็นจริง
7.1 ตัวอย่าง
for#!/bin/bash for i in $( ls ); do echo item: $i doneเป็นการนำคำสั่ง
lsไปเป็นตัวแปรชั่วคราวในการกำหนดขอบเขตให้กับตัวแปรiในคำสั่งforในที่นี้จะทำการแสดงผลว่าitem: FILENAME ...7.2 ตัวอย่าง
forอีกแบบ#!/bin/bash for i in `seq 1 10`; do echo $i doneเป็นการนำผลจากคำสั่ง
seq 1 10ไปกำหนดขอบเขตให้กับตัวแปรiในคำสั่งforอาจเขียนเลียนแบบตัวอย่าง 7.1 ได้เหมือนกันดังนี้#!/bin/bash for i in $( seq 1 10 ); do echo $i done7.3 ตัวอย่าง
while#!/bin/bash COUNTER=0 while [ $COUNTER -lt 10 ]; do echo The counter is $COUNTER let COUNTER=COUNTER+1 doneเป็นการแสดงค่าตัวแปร
COUNTERที่เพิ่มขึ้นทีละ 1 จาก 0 ถึง 9 โปรดสังเกตุการใช้ตัวแปรเก็บค่าตัวเลข, การเปรียบเทียบตัวเลขโดยใช้ตัวเปรียบเทียบ-lt(less than) และการกำหนดเพิ่มค่าให้กับตัวแปรแบบตัวเลขโดยใช้คำสั่งlet7.4 ตัวอย่าง
until#!/bin/bash COUNTER=20 until [ $COUNTER -lt 10 ]; do echo COUNTER $COUNTER let COUNTER-=1 doneจะแสดงตัวเลขตั้งแต่ 20 ลดลงทีละ 1 จนถึง 10
8.ฟังก์ชั่น (functions)
ในการใช้งานเชลล์สคริปต์แบบจริงจัง เราจำเป็นต้องเขียนฟังก์ชั่นเพื่อประโยชน์ในการเรียกใช้งานแบบซ้ำ ๆ เพื่อให้ประหยัดการเขียนโค๊ด และให้โค๊ดดูง่าย
มีรูปแบบเป็นfunction FUNCTION_NAME { COMMAND }หรือ
FUNCTION_NAME () { COMMAND }โปรแกรมจะเว้นไม่ถูกเรียกทำงานในช่วงตั้งแต่ชื่อฟังก์ชั่นจนกระทั่งจบบล๊อก
{ COMMAND }
เรานิยมวางฟังก์ชั่นไว้ที่ต้นโปรแกรม เพื่อให้สามารถถูกเรียกจากโค๊ดหลักได้
8.1 ตัวอย่างฟังก์ชั่น
#!/bin/bash function quit { exit } function hello { echo Hello! } hello quit echo fooตัวอย่างนี้ บรรทัดที่ 10 คือคำสั่ง
echo fooจะไม่ถูกเรียกใช้ เนื่องจากโปรแกรมจะหลุดสู่เชลล์ในบรรทัดที่ 9 คือคำสั่งquit
8.2 ตัวอย่างฟังก์ชั่นที่มีการส่งผ่านค่าตัวแปร
#!/bin/bash function quit { exit } function ex { echo $1 } ex Hello ex World quit echo fooจากตัวอย่าง จะเห็นการส่งผ่านข้อความเข้าไปในฟังก์ชั่น
exด้วยตัวแปร$1
ในทำนองเดียวกัน ถ้ามีการส่งผ่านตัวแปรหลายตัว ก็จะใช้รูปแบบเป็น$2, $3, ...
โดยเรียกใช้งานด้วยรูปแบบex VAR1 VAR2 VAR3 ...ตามลำดับ9.การติดต่อผู้ใช้ (User Interfaces)
9.1 ใช้คำสั่ง
selectในการสร้างหัวข้อให้เลือก#!/bin/bash OPTIONS="Hello Quit" select opt in $OPTIONS; do if [ "$opt" = "Quit" ]; then echo done exit elif [ "$opt" = "Hello" ]; then echo Hello World else clear echo bad option fi doneตัวอย่างนี้จะสร้างหัวข้อ 1) และ 2) จากตัวแปร
OPTIONSเพื่อมาให้เลือก โดยจะวนรอบถามไปเรื่อย ๆ จนกว่าจะพบคำสั่งexitให้ออกจากการวนรอบ
9.2 ใช้การตรวจสอบว่ามีการใส่ค่าพารามิเตอร์หรือไม่
#!/bin/bash if [ -z "$1" ]; then echo usage: $0 directory exit fi SRCD=$1 TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCDบรรทัดที่ 2 จะตรวจว่ามีการใส่พารามิเตอร์ให้กับโปรแกรมหรือไม่ (
if [ -z "$1" ]-z หมายถึงการตรวจสอบว่ามีค่าหรือไม่)
ถ้าไม่มีการใส่ค่าพารามิเตอร์ โปรแกรมจะทำคำสั่งในบรรทัดที่ 3 คือแสดงการใช้งาน ($0คือชื่อโปรแกรมนี้) และบรรทัดที่ 4 คือออกจากโปรแกรม
แต่ถ้ามีการใส่ค่าพารามิเตอร์ถูกต้อง ก็จะทำบรรทัดที่ 6 ต่อไปจนจบ ซึ่งในที่นี้คือการบีบอัดทำสำเนาให้กับไดเรกทอรี่ที่เราให้เป็นพารามิเตอร์ ($1) ในชื่อไฟล์ว่า/var/backups/home-YYYYMMDD
9.3 หยุดถามผู้ใช้ด้วยคำสัง
read#!/bin/bash echo Please, enter your name read NAME echo "Hi $NAME!"สังเกตุการใช้คำสั่ง
readกำหนดค่าให้ตัวแปรNAMEไม่ต้องใช้เครื่องหมาย$นำหน้าตัวแปรอาจรอรับค่าทีละหลายตัวแปรได้ด้วย โดยคั่นแต่ละตัวแปรด้วยช่องว่าง
#!/bin/bash echo Please, enter your firstname and lastname read FN LN echo "Hi! $LN, $FN !"10.เกร็ดอื่น ๆ
10.1 การสั่งรันสคริปต์และคำสั่งsourceการสั่งรันสคริปต์ในเชลล์ มีเกร็ดคือ
- ถ้าเราใส่ชื่อสคริปต์พร้อมพาธ เชลล์จะค้นหาสคริปต์จากชื่อเต็มที่เราใส่ เช่น
$ /bin/ls- ถ้าเราใส่ชื่อสคริปต์โดด ๆ เชลล์จะค้นหาสคริปต์จากตัวแปร
$PATHโดยไม่สนใจไดเรคทอรี่ปัจจุบัน เช่น$ mycodeหากค้นไม่พบ จะแสดงข้อผิดพลาด
แต่หากต้องการสั่งรันสคริปต์ในไดเรคทอรี่ปัจจุบัน เราต้องใช้คำสั่งอ้างอิงคือ$ ./mycodeเมื่อสคริปต์ถูกรันจนจบแล้ว ค่าของตัวแปรต่าง ๆ ในสคริปต์จะถูกลบไปด้วย ยกเว้นถ้าเราใช้คำสั่ง
sourceหรือคำสั่ง.
เชลล์จะรันคำสั่งนั้นโดยถือเสมือนเป็นสภาพแวดล้อมเดียวกัน ดังนั้นค่าตัวแปรต่าง ๆ ในสคริปต์จะยังคงค้างอยู่ในเชลล์
โดยเมื่อใช้คำสั่งนี้แล้ว การค้นหาสคริปต์ เชลล์จะค้นหาจากตัวแปร$PATHก่อน ตามด้วยไดเรคทอรี่ปัจจุบันด้วย
เช่น ถ้าสคริปต์ mycode มีเนื้อไฟล์เป็น#!/bin/bash ABC="This is new ABC"ทดลองรันได้ดังนี้
$ ABC="Old ABC" $ echo $ABC Old ABC $ ./mycode $ echo $ABC Old ABC $ . mycode $ echo $ABC This is new ABC
10.2 การแทนค่าตัวเลข
เราใช้
$((ARITHMATIC))หรือ$[ARITHMATIC]ในการแทนค่าตัวแปร
ดังนี้$ echo $(1+1) bash: 1+1: command not found$ echo 1+1
1+1
$ echo $((1+1))
2
$ echo $[1+1]
2
10.3 bash อยู่ที่ไหน
บรรทัดเริ่มต้นของสคริปต์ หลังเครื่องหมาย
#!(hash-bang) เราต้องใส่พาธของโปรแกรม bash ให้เต็ม
สำหรับเดเบียน อยู่ที่/bin/bashอยู่แล้ว แต่หากเป็นดิสโตรอื่น อาจค้นหาว่าโปรแกรม bash อยู่ที่ไหน โดยใช้คำสั่งเหล่านี้$ which bash $ whereis bash $ find / -name bash
10.4 ดูค่าที่โปรแกรมส่งออกมา
หลายโปรแกรมของเชลล์มีการส่งค่าออกมา (Return value) อาจเพื่อแจ้งสถานะการรันว่ารันสำเร็จหรือไม่อย่างไร หรืออาจส่งออกเป็นค่าที่จะนำไปประมวลผลต่อก็ตาม เราสามารถใช้ตัวแปรพิเศษ
$?ในการดูผลลัพธ์ของโปรแกรมได้
เช่น#!/bin/bash cd /dada &> /dev/null echo rv: $? cd $(pwd) &> /dev/null echo rv: $?กรณีนี้ ไดเรคทอรี่
/dadaเป็นไดเรคทอรี่ที่เราแกล้งพิมพ์ผิดไว้ เพื่อดูว่าสคริปต์จะส่งออกค่าออกมาเป็นอย่างไร ซึ่งจะได้ผลออกมาเป็น 1 และ 0 ตามลำดับ คือ 1 หมายถึงมีข้อผิดพลาดในโปรแกรม และ 0 หมายถึงรันสำเร็จ ไม่มีข้อผิดพลาดใด ๆ
10.5 จับการแสดงผลใส่ตัวแปร
เราสามารถนำผลลัพธ์ของโปรแกรมมาใส่ในตัวแปร ด้วยการสั่งภายใต้เครื่องหมาย
`(grave accent)
เช่น#!/bin/bash DBS=`mysql -u root -e "show databases"` for b in $DBS ; do mysql -u root -e "show tables from $b" doneเป็นการนำผลลัพธ์ของคำสั่งแรกคือ
mysql -u root -e "show databases"มาใส่ในตัวแปรDBSเพื่อทำเป็นขอบเขตให้กับตัวแปรbในคำสั่งforอีกครั้งหนึ่ง
ตามตัวอย่างจะแสดงผลทุกตารางในทุกฐานข้อมูลของ mysql11. ตัวดำเนินการ (operators) และคำสั่งน่าสนใจ
11.1 ตัวดำเนินการเปรียบเทียบตัวอักษร (String comparison operators)
[ "$s1" = "$s2" ]หรือ[ "$s1" == "$s2" ]เป็นจริง ถ้า s1 เท่ากับ s2[ "$s1" != "$s2" ]เป็นจริง ถ้า s1 ไม่เท่ากับ s2[[ "$s1" < "$s2" ]]หรือ[ "$s1" \< "$s2" ]เป็นจริง ถ้า s1 น้อยกว่า s2[[ "$s1" > "$s2" ]]หรือ[ "$s1" \> "$s2" ]เป็นจริง ถ้า s1 มากกว่า s2[ -n "$s1" ]เป็นจริง ถ้า s1 มีค่าใด ๆ[ -z "$s1" ]เป็นจริง ถ้า s1 ไม่มีค่า
11.2 ตัวอย่างการเปรียบเทียบอักษร
#!/bin/bash S1='string' S2='String' if [ "$S1"="$S2" ]; then echo "S1('$S1') is not equal to S2('$S2')" fi if [ "$S1"="$S1" ]; then echo "S1('$S1') is equal to S1('$S1')" fi
11.3 ตัวดำเนินการทางคณิตศาลตร์ (Arithmetic operators)
+การบวก-การลบ*การคูณ/การหาร%การหาเศษจากตัวหาร (remainder)
11.4 ตัวเปรียบเทียบทางคณิตศาตร์ (Arithmetic relational operators
-ltน้อยกว่า (<)-gtมากกว่า (>)-leน้อยกว่าหรือเท่ากับ (<=)-geมากกว่าหรือเท่ากับ (>=)-eqเท่ากับ (==)-neไม่เท่ากับ (!=)
11.5 คำสั่งควรรู้
sed (stream editor)
sedเป็นเอดิเตอร์แบบบรรทัดคำสั่ง มีการใช้งานที่พลิกแพลงหลากหลายมาก ตัวอย่าง$ sed 's/old/new/g' /tmp/dummyนำเอาเนื้อไฟล์
/tmp/dummyมาแทนที่oldด้วยnewและแสดงออกทางจอภาพ$ sed 12,18d /tmp/dummyนำเอาเนื้อไฟล์
/tmp/dummyมาแสดงทางจอภาพ โดยเว้นไม่แสดงบรรทัดที่ 12 ถึงบรรทัดที่ 18ดูรายละเอียดเพิ่มเติมได้ที่ gentoo: Sed by example
awk (manipulation of datafiles, text retrieval and processing)
awkเป็นทั้งโปรแกรมและภาษาในการค้นหาข้อความในไฟล์จากรูปแบบที่เรากำหนดให้
สมมุติว่าไฟล์/tmp/dummyมีเนื้อไฟล์คือtest123 test tteessttตัวอย่างการใช้งานคือ
$ awk '/test/ {print}' /tmp/dummy test123 testดูรายละเอียดเพิ่มเติมได้ที่ gentoo: Awk by example
grep (print lines matching a search pattern)
grepเป็นโปรแกรมที่ใช้บ่อยในการค้นข้อความในไฟล์ และยังมีความสามารถในการสรุปผลการนับข้อความด้วย
ตัวอย่าง$ man grep | grep "standard" -c 8เป็นการค้นคำว่า standard ในการแสดงผลของคำสั่ง
man grepว่ามีอยู่กี่คำ คำตอบคือ 8ดูตัวอย่างเพิ่มเติมที่ tdlp: Examples using grep
wc (counts lines, words and bytes)
wcใช้ในการนับคำ, นับบรรทัด และนับจำนวนหน่วยความจำที่ถูกใช้ในไฟล์ เป็นไบต์
ตัวอย่าง$ wc --words --lines --bytes /tmp/dummy 3 3 22 /tmp/dummy
sort (sort lines of text files)
sortใช้จัดเรียงข้อมูล
สมมุติว่าไฟล์ /tmp/dummy มีเนื้อว่าb c aตัวอย่างคำสั่งคือ
$ sort /tmp/dummy a b cคือการนำเอาเนื้อไฟล์
/tmp/dummyมาจัดเรียง และแสดงผลออกทางจอภาพ
bc (a calculator programming language)
bcเป็นเครื่องคิดเลขแบบใช้บรรทัดคำสั่ง
ตัวอย่างเช่น$ echo 1+1 1+1 $ echo 1+1 | bc 2หรือใช้แบบโต้ตอบ
$ bc -q 1 == 5 00.05 == 0.05 1
5 != 5 0
2 ^ 8 256
sqrt(9) 3
while (i != 9) { i = i + 1; print i } 123456789
quit
tput (initialize a terminal or query terminfo database)
tputใช้ในการตั้งค่าหรือแสดงค่าต่าง ๆ ของเทอร์มินัล
เช่น$ tput cup 10 4เลื่อนเคอร์เซอร์ไปยังบรรทัดที่ 10 สดมภ์ที่ 4
$ tput resetล้างจอภาพ มีค่าเท่ากับคำสั่ง
clear$ tput colsแสดงจำนวนสดมภ์ (ความกว้าง) ของจอเทอร์มินัล
12.ตัวอย่างสคริปต์
12.1 ตัวอย่างสคริปต์ดูรายชื่อไฟล์ในไดเรคทอรี่ย่อย
#!/bin/bash function listdir { local PAT="$1" local ROOT="$2" for i in *; do if [ -d "$i" ]; then local CUR="$ROOT/$i" pushd "$i" &>/dev/null listdir "$PAT" "$CUR" popd &>/dev/null fi done if [ ! -z "$( ls -d $PAT 2>/dev/null )" ]; then echo "Directory: $ROOT" ls -d $PAT 2>/dev/null echo fi }if [ -z “$1” ]; then
echo List file in PATTERN recursive into directories.
echo Usage: $0 “PATTERN”
exit
fi
PATTERN=”$1″
echo “List $PATTERN”
listdir “$PATTERN” “.”ให้ผลคล้ายคำสั่ง
$ find * -name PATTERN
12.2 ตัวอย่างสคริปต์บีบอัดสำรองข้อมูล
#!/bin/bash SRCD="/home/" TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD
12.3 เปลี่ยนชื่อไฟล์ทีละหลายไฟล์
#!/bin/sh # renna: rename multiple files according to several rules # written by felix hudson Jan - 2000#first check for the various ‘modes’ that this program has
#if the first ($1) condition matches then we execute that portion of the
#program and then exit# check for the prefix condition
if [ $1 = p ]; then#we now get rid of the mode ($1) variable and prefix ($2)
prefix=$2 ; shift ; shift# a quick check to see if any files were given
# if none then its better not to do anything than rename some non-existent
# files!!if [$1 = ]; then
echo “no files given”
exit 0
fi# this for loop iterates through all of the files that we gave the program
# it does one rename per file given
for file in $*
do
mv ${file} $prefix$file
done#we now exit the program
exit 0
fi# check for a suffix rename
# the rest of this part is virtually identical to the previous section
# please see those notes
if [ $1 = s ]; then
suffix=$2 ; shift ; shiftif [$1 = ]; then
echo “no files given”
exit 0
fifor file in $*
do
mv ${file} $file$suffix
doneexit 0
fi# check for the replacement rename
if [ $1 = r ]; thenshift
# i included this bit as to not damage any files if the user does not specify
# anything to be done
# just a safety measureif [ $# -lt 3 ] ; then
echo “usage: renna r [expression] [replacement] files… ”
exit 0
fi# remove other information
OLD=$1 ; NEW=$2 ; shift ; shift# this for loop iterates through all of the files that we give the program
# it does one rename per file given using the program ‘sed’
# this is a sinple command line program that parses standard input and
# replaces a set expression with a give string
# here we pass it the file name ( as standard input) and replace the nessesary
# textfor file in $*
do
new=`echo ${file} | sed s/${OLD}/${NEW}/g`
mv ${file} $new
done
exit 0
fi# if we have reached here then nothing proper was passed to the program
# so we tell the user how to use it
echo “usage;”
echo ” renna p [prefix] files..”
echo ” renna s [suffix] files..”
echo ” renna r [expression] [replacement] files..”
exit 0# done!
12.4 เปลี่ยนชื่อไฟล์แบบง่าย
#!/bin/bash # renames.sh # basic file renamercriteria=$1
re_match=$2
replace=$3for i in $( ls *$criteria* );
do
src=$i
tgt=$(echo $i | sed -e “s/$re_match/$replace/”)
mv $src $tgt
done13. การค้นหาที่ผิดในสคริปต์
เราใช้พารามิเตอร์
-xต่อท้ายคำสั่งในบรรทัดแรก#!/bin/bash -xจะมีผลว่าเชลล์จะแสดงทุกคำสั่งที่ถูกรันออกมาทางจอภาพ
จบแล้วจ้า