คำสั่งวนซ้ำใช้เพื่อบังคับให้โปรแกรมรันคำสั่งซ้ำๆ คำสั่งที่ดำเนินการเรียกว่าเนื้อความวนรอบ
ลูปดำเนินการจนกว่าค่าของนิพจน์ควบคุมจะเป็น 0 นิพจน์ควบคุมอาจเป็นประเภทข้อมูลสเกลาร์ใดก็ได้
ภาษาเชลล์ยังมีคำสั่งวนซ้ำหรือวนซ้ำหลายรายการ ในบทความนี้ให้เราตรวจสอบคำสั่งวนรอบซึ่ง bash ให้โดยใช้ตัวอย่าง
Bash รองรับคำสั่งวนซ้ำสามประเภทต่อไปนี้
- สำหรับวนซ้ำ
- ขณะวนซ้ำ
- จนกว่าจะวนซ้ำ
บทความนี้เป็นส่วนหนึ่งของชุดการสอน Bash ที่กำลังดำเนินอยู่
สามารถซ้อนลูปได้ เช่นเดียวกับภาษาการเขียนโปรแกรมอื่น ๆ bash ยังรองรับคำสั่ง break เพื่อออกจากลูปปัจจุบันและดำเนินการคำสั่งต่อเพื่อดำเนินการซ้ำครั้งต่อไปของคำสั่งลูป
Bash For Loop – วิธีแรก
สำหรับลูปมักจะใช้เมื่อทราบจำนวนการวนซ้ำก่อนเข้าสู่ bash loop Bash รองรับ for loop สองประเภท รูปแบบแรกของ bash for loop คือ:
for varname in list do commands ##Body of the loop done
ในไวยากรณ์ด้านบน:
- สำหรับ ใน ทำ และทำ เป็นคำหลัก
- รายการ คือ รายการใดๆ ที่มีรายการของรายการ
- varname คือชื่อตัวแปรของ Bash
ในแบบฟอร์มนี้ คำสั่ง for จะดำเนินการคำสั่งที่อยู่ในเนื้อหา หนึ่งครั้งสำหรับแต่ละรายการในรายการ รายการปัจจุบันจากรายการจะถูกเก็บไว้ในตัวแปร "varname" แต่ละครั้งผ่านลูป varname นี้สามารถประมวลผลในเนื้อหาของลูปได้ รายการนี้สามารถเป็นตัวแปรที่มีคำหลายคำคั่นด้วยช่องว่าง หาก list หายไปในคำสั่ง for จะใช้พารามิเตอร์ตำแหน่งที่ส่งผ่านไปยังเชลล์
Bash For Loop Example 1. Unzip ไฟล์ Zip ทั้งหมด
ตัวอย่างต่อไปนี้จะค้นหารายการไฟล์ที่ตรงกับ “*.zip*” ในไดเร็กทอรีราก และสร้างไดเร็กทอรีใหม่ในตำแหน่งเดียวกันกับที่มีไฟล์ zip และคลายซิปเนื้อหาไฟล์ zip
# cat zip_unzip.sh #! /bin/bash # Find files which has .zip for file in `find /root -name "*.zip*" -type f` do # Skip the extension .zip dirname=`echo ${file} | awk -F'.' '{print $1}'` # Create the directory mkdir $dirname # Copy the zip file cp ${file} ${dirname} cd $dirname # Unzip the zip file from newly created directory unzip ${dirname}/$(echo ${file##/*/}) done
- ในตัวอย่างนี้ คำสั่ง find จะคืนค่ารายการไฟล์ ซึ่งแต่ละไฟล์จะถูกประมวลผลผ่านลูป
- สำหรับแต่ละรายการ จะสร้างไดเร็กทอรีด้วยชื่อของไฟล์ zip และคัดลอกไฟล์ zip ไปยังไดเร็กทอรีที่สร้างขึ้นใหม่ และแตกไฟล์ zip จากที่นั่น
- คำสั่ง echo echo ${file##/*/} ให้เฉพาะชื่อไฟล์เท่านั้น ไม่ใช่พาธ
# ./zip_unzip.sh Archive: /root/test2/test2.zip extracting: t1/p extracting: t1/q extracting: t1/r extracting: t1/s extracting: t1/t extracting: t1/u extracting: t1/v Archive: /root/test1/test1.zip extracting: t/a extracting: t/b extracting: t/c extracting: t/d extracting: t/e
คล้ายกับ Bash loop Awk ยังมี for loop และ while loop ตามที่เราพูดถึงในบทความ Awk While และ For Loop
Bash For Loop – วิธีที่สอง
รูปแบบที่สองของ for loop คล้ายกับ for loop ในภาษาการเขียนโปรแกรม 'C' ซึ่งมีสามนิพจน์ (การกำหนดค่าเริ่มต้น เงื่อนไข และการอัปเดต)
for (( expr1; expr2; expr3 )) do commands done
- ใน bash ด้านบนสำหรับไวยากรณ์คำสั่ง ก่อนการวนซ้ำครั้งแรก expr1 จะถูกประเมิน โดยปกติจะใช้เพื่อเริ่มต้นตัวแปรสำหรับลูป
- คำสั่งทั้งหมดระหว่าง do และ done จะถูกดำเนินการซ้ำๆ จนกว่าค่าของ expr2 จะเป็น TRUE
- หลังจากการวนซ้ำแต่ละครั้งจะมีการประเมิน expr3 โดยปกติแล้วจะใช้เพื่อเพิ่มตัวนับลูป
ตัวอย่างต่อไปนี้สร้างตัวเลขสุ่มจำนวน n ตัว
ทุบตีตัวอย่าง 2 สร้างตัวเลขสุ่ม n ตัว
$ cat random.sh #! /bin/bash echo -e "How many random numbers you want to generate" read max for (( start = 1; start <= $max; start++ )) do echo -e $RANDOM done $ ./random.sh How many random numbers you want to generate 5 6119 27200 1998 12097 9181
ในข้อมูลโค้ดด้านบน for loop จะสร้างตัวเลขสุ่มที่จำนวนครั้งสูงสุด RANDOM เป็นฟังก์ชันทุบตีภายในที่คืนค่าจำนวนเต็มแบบสุ่มในแต่ละคำขอ
ทุบตีขณะวนซ้ำ
คำสั่งวนซ้ำอื่นที่นำเสนอโดยภาษาโปรแกรมเชลล์คือคำสั่ง while
Syntax: while expression do commands done
ในไวยากรณ์ลูปด้านบน:
- ในขณะที่ ทำ เสร็จสิ้น คือคีย์เวิร์ด
- นิพจน์คือนิพจน์ใดๆ ที่คืนค่าสเกลาร์
- ในขณะที่คำสั่งทำให้บล็อกของโค้ดถูกเรียกใช้งานในขณะที่นิพจน์เงื่อนไขที่ให้มานั้นเป็นจริง
Bash while Example 3 เขียนเนื้อหาลงในไฟล์
ตัวอย่างต่อไปนี้จะอ่านข้อมูลจาก stdout และเขียนลงในไฟล์
$ cat writefile.sh #! /bin/bash echo -e "Enter absolute path of the file name you want to create" read file while read line do echo $line >> $file done $ sh writefile.sh Enter absolute path of the file name you want to create /tmp/a while for until $ cat /tmp/a while for until
ตัวอย่างข้างต้น อ่านชื่อไฟล์จากผู้ใช้ และอ่านบรรทัดข้อมูลจาก stdin และต่อท้ายแต่ละบรรทัดกับชื่อไฟล์ที่กำหนด เมื่อ EOF เข้ามา การอ่านจะล้มเหลว ดังนั้นการวนซ้ำจึงสิ้นสุดที่นั่น
หากคุณกำลังเขียน bash script จำนวนมาก คุณสามารถใช้ตัวแก้ไข Vim เป็น Bash IDE โดยใช้ปลั๊กอิน Vim bash-support ดังที่เราได้กล่าวไว้ก่อนหน้านี้
ทุบตีในขณะที่ตัวอย่างที่ 4 อ่านเนื้อหาของไฟล์
ในตัวอย่างก่อนหน้านี้ จะอ่านข้อมูลจาก stdout และเขียนลงในไฟล์ ในตัวอย่างนี้ จะอ่านไฟล์
เนื้อหาและเขียนลงใน stdout
$ cat read.sh #! /bin/bash echo -e "Enter absolute path of the file name you want to read" read file exec <$file # redirects stdin to a file while read line do echo $line done $ ./read.sh Enter absolute path of the file name you want to read /tmp/a while for until
ในตัวอย่างนี้ ชื่อไฟล์จะอ่าน และใช้ exec จะเปลี่ยนเส้นทาง stdin ไปยังไฟล์ จากจุดนั้น stdin ทั้งหมดมาจากไฟล์นั้น แทนที่จะเป็นคีย์บอร์ด คำสั่ง read อ่านบรรทัดจาก stdin ดังนั้นในขณะที่ลูปอ่าน stdin จนกว่า EOF จะเกิดขึ้น
ทุบจนวนซ้ำ
คำสั่ง until คล้ายกันมากในด้านไวยากรณ์และฟังก์ชันกับคำสั่ง while ข้อแตกต่างที่แท้จริงเพียงอย่างเดียวระหว่างทั้งสองคือ คำสั่ง until รันบล็อกโค้ดในขณะที่นิพจน์เงื่อนไขเป็นเท็จ และคำสั่ง while รันบล็อกโค้ดในขณะที่นิพจน์เงื่อนไขเป็นจริง
syntax: until expression do commands #body of the loop done
ใน bash ด้านบนจนถึง syntax:
where until, do, done คือคีย์เวิร์ด
นิพจน์เงื่อนไขใดๆ
ทุบตีจนถึงตัวอย่างที่ 5 ตรวจสอบไฟล์บันทึก
ตัวอย่างนี้ตรวจสอบขนาดของไฟล์บันทึก เมื่อขนาดไฟล์บันทึกถึง 2000 ไบต์ ระบบจะคัดลอกไฟล์บันทึกนั้นไป
$ cat monitor.sh file=/tmp/logfile until [ $(ls -l $file | awk '{print $5}') -gt 2000 ] do echo "Sleeping for next 5 seconds" sleep 5 done date=`date +%s` cp $file "$file-"$date.bak $ ./monitor.sh Sleeping for next 5 seconds Sleeping for next 5 seconds $ ls -l /tmp/logfile* -rw-r--r-- 1 sss sss 2010 Jun 24 12:29 logfile -rw-r--r-- 1 sss sss 2005 Jun 24 16:09 logfile-1277474574.bak
คำสั่ง until ยังคงดำเนินการเนื้อหาของลูปต่อไป จนกว่าเงื่อนไขจะเป็นจริง ในตัวอย่างนี้ เงื่อนไขคือขนาดของไฟล์ที่มากกว่า 2,000 ไบต์ ดังนั้นจึงคัดลอกไฟล์เมื่อถึง 2,000 ไบต์
นอกจากนี้ อย่าลืมอ้างอิงถึงตัวอย่าง Bash Array ก่อนหน้าของเรา
ทุบตีจนถึงตัวอย่างที่ 6 รอให้เครื่องขึ้นมา
ตัวอย่างนี้ใช้เพื่อรอจนกว่าเครื่องจะทำงานก่อนที่จะทำ ssh กับเครื่องนั้น คำสั่ง until loop จะสิ้นสุดเมื่อ ping ตอบกลับเท่านั้น
$ cat mac_wait.sh #! /bin/bash read -p "Enter IP Address:" ipadd echo $ipadd until ping -c 1 $ipadd do sleep 60; done ssh $ipadd $./mac_wait.sh Enter IP Address:192.143.2.10 PING 192.143.2.10 (192.143.2.10) 56(84) bytes of data. --- 192.143.2.10 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms PING 192.143.2.10 (192.143.2.10) 56(84) bytes of data. 64 bytes from 192.143.2.10: icmp_seq=1 ttl=64 time=0.059 ms --- 192.143.2.10 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.059/0.059/0.059/0.000 ms The authenticity of host '192.143.2.10 (192.143.2.10)' can't be established. Are you sure you want to continue connecting (yes/no)? yes
จนกว่าการวนซ้ำจะมีประโยชน์มากที่บรรทัดคำสั่ง เพื่อเป็นการรอให้เหตุการณ์บางอย่างเกิดขึ้น