เขียน Shell Script

By | 01/08/2013

ขอบคุณ 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 Guide

1.เกริ่น

เชลล์สคริปต์ พูดง่าย ๆ ก็คือการนำคำสั่งในเชลล์ของลินุกซ์มาเรียงต่อกันให้ทำงานตามที่เราต้องการ โดยเพิ่มโครงสร้างการวนรอบ และฟังก์ชั่นต่าง ๆ เติมเข้ามา เพื่อให้การทำงานได้ตามที่เราต้องการ ซึ่งจะเหมาะมากกับงานแบบ batch หรืองานแบบ schedule

ฉะนั้นการที่จะเขียนโค๊ดให้ได้ดี จึงต้องศึกษาจดจำคำสั่งต่าง ๆ ของเชลล์ให้ได้เท่าที่เราต้องการใช้งาน (จำหมดคงไม่ไหว)
คำสั่งต่าง ๆ สามารถดูได้ที่ gnu.org: Bash Reference Manual

สำหรับเดเบียน หากต้องการใช้งาน bash แบบเต็มรูป (ไม่อั้นความสามารถ) อาจต้องปรับแต่งเล็กน้อย
เปลี่ยนให้เชลล์ของเราเป็น bash แทน sh ใช้คำสั่ง

$ chsh -s /bin/bash

สำหรับเอดิเตอร์ ถ้าใช้ vi ควรติดตั้ง vim-full และอย่าลืมแก้ไขไฟล์ vimrc ให้แสดงสีด้วย เพื่อให้ดูโค๊ดได้ง่ายขึ้น

$ sudo aptitude install vim-full  $ vi ~/.vimrc
syntax on
:wq

2.เริ่มเขียน

 

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 เป็นการบอกให้เชลล์รู้ว่า โค๊ดที่เราเขียนนี้จะถูกประมวลผลด้วยโปรแกรมอะไร ในที่นี้คือ

1
/bin/bash

บรรทัดที่สอง เป็นการสั่งให้พิมพ์

1
Hello World

ออกทางจอภาพ

 

2.2 สคริปต์สำหรับสำรองข้อมูล

จากตัวอย่างข้างบน ผมเขียนอธิบายโดยละเอียดโดยใช้เอดิเตอร์ vi แต่เพื่อให้กระชับเข้า จะขอละเลยการใช้เอดิเตอร์ โดยจะเขียนเฉพาะโค๊ดอย่างเดียวครับ

#!/bin/bash            tar -cvzf /tmp/my-backup.tgz /home/USER/

บรรทัดที่สองให้เปลี่ยนคำว่า USER เป็นชื่อเรา
เป็นการสั่งให้ใช้คำสั่ง tar ทำการสำรองข้อมูลพร้อมบีบอัดข้อมูลในไดเรคทอรี่ของบ้านเราไปสู่ไฟล์ชื่อ

1
/tmp/my-backup.tgz

3. การเปลี่ยนทิศข้อมูล (Redirection)

ใช้สัญญลักษณ์

1
>

ใสการเปลี่ยนทิศ

3.1 ข้อมูลมาตรฐาน

ข้อมูลมาตรฐานในเชลล์จะมีอยู่ 4 ชนิด คือข้อมูลเข้า(stdin), ข้อมูลแสดงผล(stdout), ข้อมูลข้อผิดพลาด(stderr), และแฟ้มข้อมูล(file)
ในทางปฏิบัติ เราสามารถเปลี่ยนทิศทางของข้อมูลเหล่านี้ไปมาได้ โดยมีมาตรฐานคือ 1 จะหมายถึงข้อมูลแสดงผล(stdout) และ 2 จะหมายถึงข้อมูลความผิดพลาด(stderr)

เช่น

 

3.2 ตัวอย่างเปลี่ยน stdout ไปเป็น file

$ ls -l > ls-l.txt

จะเปลี่ยนการแสดงผลของคำสั่ง

1
ls -l

ไปเก็บไว้ที่ไฟล์ชื่อ ls-l.txt ดังนั้นคำสั่งตามตัวอย่างนี้จะไม่แสดงอะไรออกมาทางจอภาพ แต่จะเก็บไว้ที่ไฟล์แทน หากเราต้องการดูผล สามารถใช้คำสั่งแสดงผลของไฟล์ได้คือ

$ cat ls-l.txt

 

3.3 ตัวอย่างเปลี่ยน stderr ไปเป็น file

$ grep da * 2> grep-errors.txt

ตัวอย่างนี้เป็นการค้นหาข้อความ

1
da

ในทุกไฟล์ (*) และหากเกิดข้อผิดพลาดขึ้น จะนำข้อความผิดพลาดไปเก็บไว้ที่ไฟล์ชื่อ grep-errors.txt

 

3.4 ตัวอย่างเปลี่ยน stdout ไปเป็น stderr

$ grep da * 1>&2

เป็นการค้นหาข้อความ

1
da

ในทุกไฟล์ (*) โดยนำการแสดงผลไปใส่ไว้ใน stderr แทนการแสดงผลปกติ แต่ในกรณีนี้เราป้อนคำสั่งทางแป้นพิมพ์ stdout และ stderr คือจอภาพเหมือนกัน จึงไม่เห็นความแตกต่าง แต่หากคำสั่งนี้ไปอยู่ในสคริปต์ที่เรากำหนดให้ stderr เป็นไฟล์ error-log การแสดงผลก็จะถูกเปลี่ยนทิศไปตามนั้น

 

3.5 ตัวอย่างเปลี่ยน stderr ไปเป็น stdout

$ grep da * 2>&1

เป็นการค้นหาข้อความ

1
da

ในทุกไฟล์ (*) โดยหากเกิดข้อผิดพลาดขึ้น จะแสดงผลข้อผิดพลาดออกมาทาง stdout ซึ่งในที่นี้คือจอภาพเหมือนกัน

 

3.6 ตัวอย่างเปลี่ยน stderr และ stdout ไปยัง file

$ rm -f $(find /home/USER -name core) &> /dev/null

คำสั่งนี้เป็นการค้นหาไฟล์ในไดเรคทอรี่

1
/home/USER

ที่มีชื่อว่า core (

1
find /home/USER -name core

)
เมื่อพบแล้วก็จัดการลบทิ้งโดยไม่เตือน (

1
rm -f

)
โดยโยกการแสดงผลทั้งหมด (ทั้ง stderr และ stdout – ใช้สัญญลักษณ์

1
&>

) ไปยังไฟล์ชื่อ

1
/dev/null

ซึ่งเป็นไฟล์พิเศษ หมายความว่ายกเลิกการแสดงผลทั้งหมด
(คำสั่งนี้ค่อนข้างอันตราย เพราะลบโดยไม่เตือน โปรดทดลองด้วยความระมัดระวังครับ)

4. การส่งต่อผลลัพธ์ หรือ ไปป์ (Pipes)

 

4.1 ความหมาย

ไปป์เป็นการส่งต่อผลลัพธ์จากคำสั่งหนึ่งไปเป็นค่านำเข้าของอีกคำสั่งหนึ่ง

 

4.2 ตัวอย่างไปป์

$ ls -l | sed -e "s/[aeio]/u/g"

ตัวอย่างนี้จะนำเอาผลลัพธ์ที่ได้จากคำสั่ง

1
ls -l

ส่งต่อไปให้คำสั่ง

1
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$"

ตัวอย่างนี้จะส่งผลลัพธ์จากคำสั่ง

1
ls -l

ต่อไปให้คำสั่ง

1
grep "\.txt$"

คือให้แสดงเฉพาะไฟล์ที่มีนามสกุลเป็น

1
.txt

เท่านั้น
มีค่าเท่ากับคำสั่ง ls แบบใส่พารามิเตอร์กรอง

$ ls -l *.txt

หมายเหตุ
รูปแบบ

1
"\.txt$"

เป็นรูปแบบของ Regular Expression ซึ่งใช้มากในเชลล์สคริปต์ มีความหมายว่า “ที่ต้องลงท้ายด้วย .txt”

5. ตัวแปร (Variables)

ตัวแปรในเชลล์สคริปต์ ไม่มีชนิดข้อมูล คือเราสามารถใช้ตัวแปรแทนตัวเลขหรืออักขระใด ๆ ก็ได้
โดยในขั้นตอนกำหนดค่า ไม่ต้องใช้เครื่องหมายใด ๆ นำหน้า แต่ตอนอ้างถึง ต้องใช้เครื่องหมาย

1
$

นำหน้าตัวแปร

 

5.1 ตัวอย่างสคริปต์ Hello World แบบใช้ตัวแปร

#!/bin/bash            STR="Hello World!"  echo $STR

ให้ผลลัพธ์เหมือนตัวอย่างที่ 2.1
ข้อควรระวังคือ

  • การกำหนดค่าให้ตัวแปร อย่าเว้นวรรคระหว่างตัวแปรกับเครื่องหมาย
    1
    =
  • หากลืมใส่เครื่องหมาย
    1
    $

    จะหมายถึงการแสดงผลข้อความว่า

    1
    STR

    เฉย ๆ

 

 

5.2 ตัวอย่างสคริปต์สำรองข้อมูลแบบใช้ตัวแปร

#!/bin/bash            OF=/tmp/my-backup-$(date +%Y%m%d).tgz  tar -cvzf $OF /home/USER/

ให้ผลลัพธ์คล้ายตัวอย่าง 2.2 แต่เพิ่มการใช้ตัวแปรลอยในคำสั่ง

1
$(date +%Y%m%d)

ซึ่งมีผลทำให้ชื่อไฟล์ข้อมูลสำรองมีวันที่ต่อท้ายชื่อด้วย

 

5.3 ตัวแปรท้องถิ่น

ตัวแปรในเชลล์สคริปต์ทุกตัว จะเป็นตัวแปรรวม (Global) คือทุก ๆ ส่วนของโปรแกรมจะเห็นเหมือนกันหมด
แต่ในกรณีที่เราต้องการให้เห็นเฉพาะในฟังก์ชั่นที่เราต้องการ เราสามารถกำหนดให้ตัวแปรเป็นตัวแปรท้องถิ่นได้ด้วยคำสั่ง

1
local

เช่น

#!/bin/bash  HELLO=Hello   function hello {          local HELLO=World          echo $HELLO  }

echo $HELLO
hello
echo $HELLO

สคริปต์นี้ตัวแปร

1
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 ตัวอย่าง

1
if ... then

#!/bin/bash  if [ "foo" = "foo" ]; then      echo expression evaluated as true  fi

โค๊ดนี้จะเป็นจริงเสมอ ดังนั้นข้อความ ”

1
expression evaluated as true

” จะถูกพิมพ์ออกมาเสมอ

 

6.3 ตัวอย่าง

1
if ... then ... else

#!/bin/bash  if [ "foo" = "foo" ]; then     echo expression evaluated as true  else     echo expression evaluated as false  fi

โค๊ดนี้จะเป็นจริงเสมอ ดังนั้นข้อความ ”

1
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

คำสั่ง

1
for

มีลักษณะคล้าย for ในภาษาไพธอน มีรูปแบบเป็น

for VAR in SCOPE; do      COMMAND  done

คำสั่ง

1
while

มีรูปแบบเป็น

while [CONDITION]; do      COMMAND  done

ถ้าเงื่อนไข CONDITION เป็นจริง ก็จะทำคำสั่ง COMMAND คำสั่ง

1
until

รูปแบบตรงกันข้ามกับ while โดยมีรูปแบบเป็น

until [CONDITION]; do      COMMAND  done

คือจะทำคำสั่ง COMMAND จนกว่าเงื่อนไข CONDITION จะเป็นจริง

7.1 ตัวอย่าง

1
for

#!/bin/bash  for i in $( ls ); do      echo item: $i  done

เป็นการนำคำสั่ง

1
ls

ไปเป็นตัวแปรชั่วคราวในการกำหนดขอบเขตให้กับตัวแปร

1
i

ในคำสั่ง

1
for

ในที่นี้จะทำการแสดงผลว่า

1
item: FILENAME ...

7.2 ตัวอย่าง

1
for

อีกแบบ

#!/bin/bash  for i in `seq 1 10`; do      echo $i  done

เป็นการนำผลจากคำสั่ง

1
seq 1 10

ไปกำหนดขอบเขตให้กับตัวแปร

1
i

ในคำสั่ง

1
for

อาจเขียนเลียนแบบตัวอย่าง 7.1 ได้เหมือนกันดังนี้

#!/bin/bash  for i in $( seq 1 10 ); do      echo $i  done

7.3 ตัวอย่าง

1
while

#!/bin/bash   COUNTER=0  while [  $COUNTER -lt 10 ]; do      echo The counter is $COUNTER      let COUNTER=COUNTER+1   done

เป็นการแสดงค่าตัวแปร

1
COUNTER

ที่เพิ่มขึ้นทีละ 1 จาก 0 ถึง 9 โปรดสังเกตุการใช้ตัวแปรเก็บค่าตัวเลข, การเปรียบเทียบตัวเลขโดยใช้ตัวเปรียบเทียบ

1
-lt

(less than) และการกำหนดเพิ่มค่าให้กับตัวแปรแบบตัวเลขโดยใช้คำสั่ง

1
let

7.4 ตัวอย่าง

1
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  }

โปรแกรมจะเว้นไม่ถูกเรียกทำงานในช่วงตั้งแต่ชื่อฟังก์ชั่นจนกระทั่งจบบล๊อก

1
{ COMMAND }

เรานิยมวางฟังก์ชั่นไว้ที่ต้นโปรแกรม เพื่อให้สามารถถูกเรียกจากโค๊ดหลักได้

 

8.1 ตัวอย่างฟังก์ชั่น

#!/bin/bash   function quit {      exit  }  function hello {      echo Hello!  }  hello  quit  echo foo

ตัวอย่างนี้ บรรทัดที่ 10 คือคำสั่ง

1
echo foo

จะไม่ถูกเรียกใช้ เนื่องจากโปรแกรมจะหลุดสู่เชลล์ในบรรทัดที่ 9 คือคำสั่ง

1
quit

 

8.2 ตัวอย่างฟังก์ชั่นที่มีการส่งผ่านค่าตัวแปร

#!/bin/bash   function quit {      exit  }    function ex {      echo $1   }    ex Hello  ex World  quit  echo foo

จากตัวอย่าง จะเห็นการส่งผ่านข้อความเข้าไปในฟังก์ชั่น

1
ex

ด้วยตัวแปร

1
$1

ในทำนองเดียวกัน ถ้ามีการส่งผ่านตัวแปรหลายตัว ก็จะใช้รูปแบบเป็น

1
$2, $3, ...

โดยเรียกใช้งานด้วยรูปแบบ

1
ex VAR1 VAR2 VAR3 ...

ตามลำดับ

9.การติดต่อผู้ใช้ (User Interfaces)

 

9.1 ใช้คำสั่ง

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) จากตัวแปร

1
OPTIONS

เพื่อมาให้เลือก โดยจะวนรอบถามไปเรื่อย ๆ จนกว่าจะพบคำสั่ง

1
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 จะตรวจว่ามีการใส่พารามิเตอร์ให้กับโปรแกรมหรือไม่ (

1
if [ -z "$1" ]

-z หมายถึงการตรวจสอบว่ามีค่าหรือไม่)
ถ้าไม่มีการใส่ค่าพารามิเตอร์ โปรแกรมจะทำคำสั่งในบรรทัดที่ 3 คือแสดงการใช้งาน (

1
$0

คือชื่อโปรแกรมนี้) และบรรทัดที่ 4 คือออกจากโปรแกรม
แต่ถ้ามีการใส่ค่าพารามิเตอร์ถูกต้อง ก็จะทำบรรทัดที่ 6 ต่อไปจนจบ ซึ่งในที่นี้คือการบีบอัดทำสำเนาให้กับไดเรกทอรี่ที่เราให้เป็นพารามิเตอร์ (

1
$1

) ในชื่อไฟล์ว่า

1
/var/backups/home-YYYYMMDD

 

9.3 หยุดถามผู้ใช้ด้วยคำสัง

1
read

#!/bin/bash  echo Please, enter your name  read NAME  echo "Hi $NAME!"

สังเกตุการใช้คำสั่ง

1
read

กำหนดค่าให้ตัวแปร

1
NAME

ไม่ต้องใช้เครื่องหมาย

1
$

นำหน้าตัวแปร

อาจรอรับค่าทีละหลายตัวแปรได้ด้วย โดยคั่นแต่ละตัวแปรด้วยช่องว่าง

#!/bin/bash  echo Please, enter your firstname and lastname  read FN LN   echo "Hi! $LN, $FN !"

10.เกร็ดอื่น ๆ

10.1 การสั่งรันสคริปต์และคำสั่ง  
1
source

การสั่งรันสคริปต์ในเชลล์ มีเกร็ดคือ

  • ถ้าเราใส่ชื่อสคริปต์พร้อมพาธ เชลล์จะค้นหาสคริปต์จากชื่อเต็มที่เราใส่ เช่น
    $ /bin/ls
  • ถ้าเราใส่ชื่อสคริปต์โดด ๆ เชลล์จะค้นหาสคริปต์จากตัวแปร
    1
    $PATH

    โดยไม่สนใจไดเรคทอรี่ปัจจุบัน เช่น

    $ mycode

    หากค้นไม่พบ จะแสดงข้อผิดพลาด
    แต่หากต้องการสั่งรันสคริปต์ในไดเรคทอรี่ปัจจุบัน เราต้องใช้คำสั่งอ้างอิงคือ

    $ ./mycode

เมื่อสคริปต์ถูกรันจนจบแล้ว ค่าของตัวแปรต่าง ๆ ในสคริปต์จะถูกลบไปด้วย ยกเว้นถ้าเราใช้คำสั่ง

1
source

หรือคำสั่ง

1
.

เชลล์จะรันคำสั่งนั้นโดยถือเสมือนเป็นสภาพแวดล้อมเดียวกัน ดังนั้นค่าตัวแปรต่าง ๆ ในสคริปต์จะยังคงค้างอยู่ในเชลล์
โดยเมื่อใช้คำสั่งนี้แล้ว การค้นหาสคริปต์ เชลล์จะค้นหาจากตัวแปร

1
$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 การแทนค่าตัวเลข

เราใช้

1
$((ARITHMATIC))

หรือ

1
$[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 อยู่ที่ไหน

บรรทัดเริ่มต้นของสคริปต์ หลังเครื่องหมาย

1
#!

(hash-bang) เราต้องใส่พาธของโปรแกรม bash ให้เต็ม
สำหรับเดเบียน อยู่ที่

1
/bin/bash

อยู่แล้ว แต่หากเป็นดิสโตรอื่น อาจค้นหาว่าโปรแกรม bash อยู่ที่ไหน โดยใช้คำสั่งเหล่านี้

$ which bash  $ whereis bash  $ find / -name bash

 

10.4 ดูค่าที่โปรแกรมส่งออกมา

หลายโปรแกรมของเชลล์มีการส่งค่าออกมา (Return value) อาจเพื่อแจ้งสถานะการรันว่ารันสำเร็จหรือไม่อย่างไร หรืออาจส่งออกเป็นค่าที่จะนำไปประมวลผลต่อก็ตาม เราสามารถใช้ตัวแปรพิเศษ

1
$?

ในการดูผลลัพธ์ของโปรแกรมได้
เช่น

#!/bin/bash  cd /dada &> /dev/null  echo rv: $?  cd $(pwd) &> /dev/null  echo rv: $?

กรณีนี้ ไดเรคทอรี่

1
/dada

เป็นไดเรคทอรี่ที่เราแกล้งพิมพ์ผิดไว้ เพื่อดูว่าสคริปต์จะส่งออกค่าออกมาเป็นอย่างไร ซึ่งจะได้ผลออกมาเป็น 1 และ 0 ตามลำดับ คือ 1 หมายถึงมีข้อผิดพลาดในโปรแกรม และ 0 หมายถึงรันสำเร็จ ไม่มีข้อผิดพลาดใด ๆ

 

10.5 จับการแสดงผลใส่ตัวแปร

เราสามารถนำผลลัพธ์ของโปรแกรมมาใส่ในตัวแปร ด้วยการสั่งภายใต้เครื่องหมาย

1
`
1
2
 (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

เป็นการนำผลลัพธ์ของคำสั่งแรกคือ

1
mysql -u root -e "show databases"

มาใส่ในตัวแปร

1
DBS

เพื่อทำเป็นขอบเขตให้กับตัวแปร

1
b

ในคำสั่ง

1
for

อีกครั้งหนึ่ง
ตามตัวอย่างจะแสดงผลทุกตารางในทุกฐานข้อมูลของ mysql

11. ตัวดำเนินการ (operators) และคำสั่งน่าสนใจ

 

11.1 ตัวดำเนินการเปรียบเทียบตัวอักษร (String comparison operators)

  • 1
    <strong>[ "$s1" = "$s2" ]</strong>

    หรือ

    1
    <strong>[ "$s1" == "$s2" ]</strong>

    เป็นจริง ถ้า s1 เท่ากับ s2

  • 1
    <strong>[ "$s1" != "$s2" ]</strong>

    เป็นจริง ถ้า s1 ไม่เท่ากับ s2

  • 1
    <strong>[[ "$s1" &lt; "$s2" ]]</strong>

    หรือ

    1
    [ "$s1" \&lt; "$s2" ]

    เป็นจริง ถ้า s1 น้อยกว่า s2

  • 1
    <strong>[[ "$s1" &gt; "$s2" ]]</strong>

    หรือ

    1
    [ "$s1" \&gt; "$s2" ]

    เป็นจริง ถ้า s1 มากกว่า s2

  • 1
    <strong>[ -n "$s1" ]</strong>

    เป็นจริง ถ้า s1 มีค่าใด ๆ

  • 1
    <strong>[ -z "$s1" ]</strong>

    เป็นจริง ถ้า 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)

  • 1
    <strong>+</strong>

    การบวก

  • 1
    <strong>-</strong>

    การลบ

  • 1
    <strong>*</strong>

    การคูณ

  • 1
    <strong>/</strong>

    การหาร

  • 1
    <strong>%</strong>

    การหาเศษจากตัวหาร (remainder)

 

11.4 ตัวเปรียบเทียบทางคณิตศาตร์ (Arithmetic relational operators

  • 1
    <strong>-lt</strong>

    น้อยกว่า (<)

  • 1
    <strong>-gt</strong>

    มากกว่า (>)

  • 1
    <strong>-le</strong>

    น้อยกว่าหรือเท่ากับ (<=)

  • 1
    <strong>-ge</strong>

    มากกว่าหรือเท่ากับ (>=)

  • 1
    <strong>-eq</strong>

    เท่ากับ (==)

  • 1
    <strong>-ne</strong>

    ไม่เท่ากับ (!=)

 

11.5 คำสั่งควรรู้

 

sed (stream editor)

1
sed

เป็นเอดิเตอร์แบบบรรทัดคำสั่ง มีการใช้งานที่พลิกแพลงหลากหลายมาก ตัวอย่าง

$ sed 's/old/new/g' /tmp/dummy

นำเอาเนื้อไฟล์

1
/tmp/dummy

มาแทนที่

1
old

ด้วย

1
new

และแสดงออกทางจอภาพ

$ sed 12,18d /tmp/dummy

นำเอาเนื้อไฟล์

1
/tmp/dummy

มาแสดงทางจอภาพ โดยเว้นไม่แสดงบรรทัดที่ 12 ถึงบรรทัดที่ 18

ดูรายละเอียดเพิ่มเติมได้ที่ gentoo: Sed by example

 

awk (manipulation of datafiles, text retrieval and processing)

1
awk

เป็นทั้งโปรแกรมและภาษาในการค้นหาข้อความในไฟล์จากรูปแบบที่เรากำหนดให้
สมมุติว่าไฟล์

1
/tmp/dummy

มีเนื้อไฟล์คือ

test123  test  tteesstt

ตัวอย่างการใช้งานคือ

$ awk '/test/ {print}' /tmp/dummy  test123  test

ดูรายละเอียดเพิ่มเติมได้ที่ gentoo: Awk by example

 

grep (print lines matching a search pattern)

1
grep

เป็นโปรแกรมที่ใช้บ่อยในการค้นข้อความในไฟล์ และยังมีความสามารถในการสรุปผลการนับข้อความด้วย
ตัวอย่าง

$ man grep | grep "standard" -c  8

เป็นการค้นคำว่า standard ในการแสดงผลของคำสั่ง

1
man grep

ว่ามีอยู่กี่คำ คำตอบคือ 8

ดูตัวอย่างเพิ่มเติมที่ tdlp: Examples using grep

 

wc (counts lines, words and bytes)

1
wc

ใช้ในการนับคำ, นับบรรทัด และนับจำนวนหน่วยความจำที่ถูกใช้ในไฟล์ เป็นไบต์
ตัวอย่าง

$ wc --words --lines --bytes /tmp/dummy   3  3 22 /tmp/dummy

 

sort (sort lines of text files)

1
sort

ใช้จัดเรียงข้อมูล
สมมุติว่าไฟล์ /tmp/dummy มีเนื้อว่า

b  c  a

ตัวอย่างคำสั่งคือ

$ sort /tmp/dummy  a  b  c

คือการนำเอาเนื้อไฟล์

1
/tmp/dummy

มาจัดเรียง และแสดงผลออกทางจอภาพ

 

bc (a calculator programming language)

1
bc

เป็นเครื่องคิดเลขแบบใช้บรรทัดคำสั่ง
ตัวอย่างเช่น

$ echo 1+1  1+1  $ echo 1+1 | bc  2

หรือใช้แบบโต้ตอบ

$ bc -q  1 == 5  0

0.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)

1
tput

ใช้ในการตั้งค่าหรือแสดงค่าต่าง ๆ ของเทอร์มินัล
เช่น

$ tput cup 10 4

เลื่อนเคอร์เซอร์ไปยังบรรทัดที่ 10 สดมภ์ที่ 4

$ tput reset

ล้างจอภาพ มีค่าเท่ากับคำสั่ง

1
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 ; shift

if [$1 = ]; then
echo “no files given”
exit 0
fi

for file in $*
do
mv ${file} $file$suffix
done

exit 0
fi

# check for the replacement rename
if [ $1 = r ]; then

shift

# i included this bit as to not damage any files if the user does not specify
# anything to be done
# just a safety measure

if [ $# -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
# text

for 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 renamer

criteria=$1
re_match=$2
replace=$3

for i in $( ls *$criteria* );
do
src=$i
tgt=$(echo $i | sed -e “s/$re_match/$replace/”)
mv $src $tgt
done

13. การค้นหาที่ผิดในสคริปต์

เราใช้พารามิเตอร์

1
-x

ต่อท้ายคำสั่งในบรรทัดแรก

#!/bin/bash -x

จะมีผลว่าเชลล์จะแสดงทุกคำสั่งที่ถูกรันออกมาทางจอภาพ

จบแล้วจ้า

ใส่ความเห็น