ข้อมูลพื้นฐานเกี่ยวกับ Bash: ลูป ในส่วนนี้ คุณจะได้เรียนรู้เกี่ยวกับลูป for, while และ until

การวนซ้ำเป็นสิ่งที่สะดวกอย่างยิ่งเมื่อเขียนโปรแกรมหรือสคริปต์ใดๆ แม้จะจำเป็นก็ตาม พวกมันอนุญาตให้เรารันโค้ดบางส่วนตามจำนวนครั้งที่ระบุ โดยธรรมชาติแล้ว bash มีลูปหลายประเภท เราจะอธิบายวงจร สำหรับใน, สำหรับ, ในขณะที่, จนกระทั่ง- แม้ว่า for in และ for จะถือเป็นไวยากรณ์ที่แตกต่างกันของข้อความเดียวกัน แต่ในความคิดของฉัน พวกมันต่างกันมากกว่า while from until

ห่วงพร้อมตัวนับสำหรับเข้า:

วงจร สำหรับในนี่คือการวนซ้ำพร้อมตัวนับ บล็อกของโค้ดที่อยู่ในเนื้อหาของลูปจะถูกทำซ้ำหลาย ๆ ครั้งตามที่มีค่าที่มีอยู่ในรายการของ for in โอเปอเรเตอร์ และในการทำซ้ำแต่ละครั้งตัวแปรตัวนับ (ในที่นี้เรียกว่า var แต่แน่นอน คุณสามารถเรียกมันว่าอะไรก็ได้ที่คุณต้องการ) มีค่าขององค์ประกอบถัดไปของรายการ
ถ้า คำหลัก do อยู่ในบรรทัดเดียวกับคำว่า for จากนั้นหลังจากรายการอาร์กิวเมนต์ (ก่อนทำ) คุณต้องใส่เครื่องหมายอัฒภาค
แต่ละองค์ประกอบ<список>อาจมีหลายข้อโต้แย้ง สิ่งนี้มีประโยชน์เมื่อประมวลผลกลุ่มของพารามิเตอร์ ในกรณีนี้ เพื่อบังคับให้แยกวิเคราะห์ข้อโต้แย้งแต่ละรายการ<списке>คุณต้องใช้คำสั่งชุด
คุณสามารถใช้ตัวแปรเป็นรายการใน for loop ได้
ใน<списке>for loop สามารถใช้ชื่อไฟล์ ซึ่งในทางกลับกันสามารถมีอักขระไวด์การ์ดได้ สิ่งนี้มีประโยชน์มากเมื่อทำงานกับไฟล์จำนวนมาก
ถ้า<список>ไม่ได้ระบุไว้ใน for loop จากนั้นจึงใช้ตัวแปร $@ เป็นรายการอาร์กิวเมนต์บรรทัดคำสั่ง
เมื่อสร้างรายการอาร์กิวเมนต์ คุณสามารถใช้การทดแทนคำสั่งใน for loop ได้
เอาต์พุตของการวนซ้ำสามารถเปลี่ยนเส้นทางจาก stdout ไปยังไฟล์หรือที่อื่นได้ (คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับสิ่งนี้ได้โดยดูที่การเปลี่ยนเส้นทาง I/O)

ไวยากรณ์:
สำหรับ var in<список>
ทำ
<выполняемые команды>
เสร็จแล้ว

ตัวอย่าง:
สำหรับชื่อในชื่อ1 ชื่อ2 ชื่อ3 ชื่อ4
ทำ
สะท้อนชื่อ $
เสร็จแล้ว

ตัวดำเนินการวนซ้ำ สำหรับมีวิธีการเขียนอีกวิธีหนึ่ง - คล้ายกับไวยากรณ์ของตัวดำเนินการ for ในภาษา C มาก ในกรณีนี้ เมื่อเริ่มต้นตัวนับ ค่าเริ่มต้นตัวแปรหรือตัวแปรหนึ่งตัว และหลังจากแต่ละรอบของลูป เงื่อนไขจะถูกตรวจสอบ ถ้าการตรวจสอบคืนค่าเป็นจริง จากนั้นการผ่านครั้งต่อไปของลูปจะเริ่มต้นขึ้น ในบล็อก<приращение счётчиков>ค่าของตัวนับตัวแปรของเราต้องเปลี่ยนแปลง (ไม่จำเป็นต้องสูงขึ้น) เพื่อว่าเมื่อตรวจสอบเงื่อนไข ไม่ช้าก็เร็วเราจะได้ค่าเท็จ ไม่เช่นนั้นการวนซ้ำจะไม่มีวันสิ้นสุด ตัวเลือกที่สะดวกและที่สำคัญที่สุดคือคุ้นเคยหากจำเป็นต้องทำซ้ำตามจำนวนครั้งที่ระบุ

ด้วยไวยากรณ์ที่คล้ายกัน:
สำหรับ ((<инициализация счётчиков>; <проверка условия>; <приращение счётчиков>))
ทำ
<выполняемые команды>
เสร็จแล้ว

ตัวอย่าง:
สำหรับ ((var=1; var<= LIMIT ; var++))
ทำ
เสียงสะท้อน $var
เสร็จแล้ว

ในขณะที่วนซ้ำ:

นี่เป็นโครงสร้างที่ค่อนข้างง่ายที่จะตรวจสอบสภาพเบื้องหลังของผู้ปฏิบัติงาน ในขณะที่และหากเงื่อนไขนี้เป็นจริง มันจะดำเนินการบล็อกของคำสั่งที่อยู่ระหว่างคำว่า do และ Done จากนั้นจึงดำเนินการตรวจสอบเงื่อนไขอีกครั้ง หากการตรวจสอบส่งคืนเท็จ รอบจะสิ้นสุดและคำสั่งต่อไปนี้จะเริ่มดำเนินการ: เสร็จแล้ว- จำเป็นอย่างยิ่งที่จะต้องแน่ใจว่า<проверка условия>ขึ้นอยู่กับโค้ดที่ทำงานอยู่ในลูป มิฉะนั้น หากผลลัพธ์ของการตรวจสอบไม่เปลี่ยนแปลง คุณจะได้รับลูปที่ไม่สิ้นสุด
อุปกรณ์อินพุตมาตรฐานสำหรับลูป while สามารถเปลี่ยนเส้นทางไปยังไฟล์ได้โดยใช้คำสั่งการเปลี่ยนเส้นทาง< в конце цикла.

ไวยากรณ์:
ในขณะที่<Проверка условия>
ทำ
<Блок команд, обязательно меняющий переменные влияющие на проверку условия>
เสร็จแล้ว

ตัวอย่าง:
ในขณะที่ [ $var0 -eq 100 ]
ทำ
เสียงสะท้อน $var
วาร์++
เสร็จแล้ว

ผู้ดำเนินการ ในขณะที่อาจมีเงื่อนไขหลายประการ แต่มีเพียงคนสุดท้ายเท่านั้นที่จะกำหนดความเป็นไปได้ในการดำเนินวงจรต่อไป ในกรณีนี้ ไวยากรณ์ของตัวดำเนินการลูปจะแตกต่างจากรูปแบบปกติ
ไวยากรณ์(ฉันขอย้ำอีกครั้งว่าเฉพาะเงื่อนไขสุดท้ายเท่านั้นที่ส่งผลต่อการดำเนินการของลูป) :
ในขณะที่
<условие1>
<условие2>

<условиеN>
ทำ
<выполняемые команды - тело цикла>
เสร็จแล้ว

จนกระทั่งวนซ้ำ:

ผู้ดำเนินการ จนกระทั่งคล้ายกับ while มาก นอกจากนี้ยังประเมินเงื่อนไขด้วย แต่จะประมวลผลเนื้อความของลูปหากผลลัพธ์ของการคำนวณเป็นเท็จ อาจดูผิดปกติ แต่จนกระทั่งประเมินเงื่อนไขก่อนที่จะผ่านครั้งแรกของลูป เช่น ในขณะที่ ไม่ใช่หลังจากนั้น เช่นเดียวกับ for/in loops เมื่อวางคีย์เวิร์ด do ในบรรทัดเดียวกับการประกาศ loop คุณต้องแทรกอักขระ ";" ก่อนทำ
เช่นเดียวกับในกรณีก่อนหน้านี้ สิ่งสำคัญคือต้องจำไว้ว่าเงื่อนไขต้องขึ้นอยู่กับการดำเนินการในเนื้อหาของลูป ไม่เช่นนั้นสคริปต์ของเราจะไม่มีวันเสร็จสมบูรณ์

ไวยากรณ์:
จนกระทั่ง<Проверка условия>
ทำ
<Блок команд, обязательно меняющий переменные влияющие на проверку условия>
เสร็จแล้ว

ตัวอย่าง:
จนถึง [ $var0 -gt 100] # มีการตรวจสอบเงื่อนไขเมื่อเริ่มต้นการวนซ้ำ
ทำ
เสียงสะท้อน $var
วาร์--
เสร็จแล้ว

ก็น่าจะเพียงพอแล้วสำหรับตอนนี้ -

  • กลับ
  • ซึ่งไปข้างหน้า

บทความใหม่:

  • การค้นพบเครือข่ายไม่เปิดขึ้นใน Windows 7/8/2008/2012
  • ข้อผิดพลาด: แอปพลิเคชันนี้ไม่สามารถเริ่มทำงานได้เนื่องจากไม่พบหรือโหลดปลั๊กอินแพลตฟอร์ม Qt "windows"
  • การกำหนดค่าการรีสตาร์ทอัตโนมัติของกระบวนการของผู้ปฏิบัติงาน rphost.exe บนเซิร์ฟเวอร์ 1C 8.3
  • วิธีการลดขนาดของบันทึกธุรกรรม (.ldf) ใน MS SQL 2008/20012

    MS SQL เช่นเดียวกับ DBMS อุตสาหกรรมที่ดีอื่นๆ พร้อมด้วยฐานข้อมูล จะเก็บบันทึกธุรกรรมที่ช่วยให้คุณสามารถย้อนกลับสถานะ...

พื้นฐานการทุบตี ส่วนที่ 2
ฉันขอโทษสำหรับความล่าช้าที่ยาวนานระหว่างบทความ แต่เซสชันทำให้ตัวเองรู้สึกว่าในช่วงเวลาที่ไม่เหมาะสมที่สุด :)
ขอขอบคุณทุกท่านสำหรับความคิดเห็น คำวิจารณ์ และการเพิ่มเติมที่แสดงความคิดเห็นในบทความที่แล้ว
ส่วนนี้ตามที่สัญญาไว้ จะเน้นไปที่ลูป การดำเนินการทางคณิตศาสตร์ และการใช้คำสั่งภายนอก
มาเริ่มกันเลย

รอบ วง For-in

ตัวดำเนินการ for-in มีไว้สำหรับการเข้าถึงค่าที่แสดงอยู่ในรายการตามลำดับ แต่ละค่าในรายการตามลำดับจะถูกกำหนดให้กับตัวแปร
ไวยากรณ์มีดังนี้:
สำหรับตัวแปรใน value_list
ทำ
ทีม
เสร็จแล้ว

ลองดูตัวอย่างเล็กๆ น้อยๆ:

#!/bin/bash
สำหรับ i ใน 0 1 2 3 4 #เราจะสลับกันกำหนดค่าตั้งแต่ 0 ถึง 4 รวมให้กับตัวแปร $i
ทำ
echo "หมายเลขคอนโซลคือ $i" >> /dev/pts/$i #เขียนบรรทัด "หมายเลขคอนโซลคือ $i" ลงในไฟล์ /dev/pts/$i (ไฟล์เทอร์มินัลเสมือน)
เสร็จแล้ว #รอบปั่นเสร็จแล้ว
ทางออก 0

หลังจากรันตัวอย่างแล้ว บรรทัดที่มีหมายเลขจะปรากฏในคอนโซลเสมือน 5 ตัวแรก (เทอร์มินัล) ค่าจากรายการจะถูกแทนที่สลับกันเป็นตัวแปร $i และค่าของตัวแปรนี้จะถูกประมวลผลแบบวนซ้ำ

รอบ ในขณะที่วนซ้ำ

การวนซ้ำ while นั้นซับซ้อนกว่าการวนซ้ำ for-in และใช้เพื่อทำซ้ำคำสั่งตราบใดที่นิพจน์บางส่วนเป็นจริง (โค้ดส่งคืน = 0)
ไวยากรณ์ของตัวดำเนินการมีดังนี้:
ในขณะที่นิพจน์หรือคำสั่งส่งคืนโค้ดส่งคืน
ทำ
ทีม
เสร็จแล้ว

ลองดูตัวอย่างการทำงานของลูปต่อไปนี้:

#!/bin/bash
again=yes #กำหนดค่า "yes" ให้กับตัวแปรอีกครั้ง
ในขณะที่ [ "$again" = "yes" ] #เราจะวนซ้ำจนกว่า $again จะเท่ากับ "yes"
ทำ
echo "กรุณากรอกชื่อ:"
อ่านชื่อ
echo "ชื่อที่คุณป้อนคือ $name"

เอคโค่ "คุณต้องการดำเนินการต่อหรือไม่?"
อ่านอีกครั้ง
เสร็จแล้ว
สะท้อน "ลาก่อน"


และตอนนี้ผลลัพธ์ของสคริปต์:
ite@ite-เดสก์ท็อป:~$ ./bash2_primer1.sh
กรุณากรอกชื่อ:
นั่นสิ
ชื่อที่คุณป้อนคือ ite
คุณต้องการดำเนินการต่อหรือไม่?
ใช่
กรุณากรอกชื่อ:
มิฮาอิล
ชื่อที่คุณป้อนคือมิฮาอิล
คุณต้องการดำเนินการต่อหรือไม่?
เลขที่
ลาก่อน

อย่างที่คุณเห็น การวนซ้ำจะทำงานจนกว่าเราจะป้อนอย่างอื่นที่ไม่ใช่ "ใช่" ระหว่าง do และ Done คุณสามารถอธิบายโครงสร้าง โอเปอเรเตอร์ ฯลฯ ทั้งหมดจะถูกดำเนินการในลูป แต่คุณควรระวังกับการวนซ้ำนี้ หากคุณรันคำสั่งใด ๆ ในนั้นโดยไม่เปลี่ยนตัวแปรนิพจน์ คุณจะได้รับ ติดอยู่ในวงวนไม่มีที่สิ้นสุด
ตอนนี้เกี่ยวกับเงื่อนไขความจริง หลังจากผ่านไประยะหนึ่ง เช่นเดียวกับในคำสั่งแบบมีเงื่อนไข if-then-else คุณสามารถแทรกนิพจน์หรือคำสั่งใดๆ ที่ส่งคืนโค้ดส่งคืนได้ และลูปจะถูกดำเนินการจนกว่าโค้ดส่งคืน = 0! ตัวดำเนินการ "[" คล้ายคลึงกับคำสั่ง test ซึ่งจะตรวจสอบความจริงของเงื่อนไขที่ถูกส่งผ่านไป

ลองดูอีกตัวอย่างหนึ่งที่ฉันนำมาจากหนังสือ Advanced Bash Scripting ฉันชอบมันมาก :) แต่ฉันทำให้มันง่ายขึ้นเล็กน้อย ในตัวอย่างนี้ เราจะแนะนำลูป UNTIL-DO อีกประเภทหนึ่ง- นี่เป็นอะนาล็อกที่เกือบจะสมบูรณ์ของลูป WHILE-DO เพียงแต่ถูกดำเนินการในขณะที่นิพจน์บางส่วนเป็นเท็จ
นี่คือตัวอย่าง:

#!/bin/bash
echo "ป้อนตัวเศษ:"
อ่านเงินปันผล
echo "ป้อนตัวส่วน:"
อ่านตัวหาร

Dnd=$dividend #เราจะเปลี่ยนตัวแปรเงินปันผลและตัวหาร
#มาเก็บความรู้ไว้ในตัวแปรอื่นๆ กันดีกว่า เพราะ... พวกเขาให้เรา
#จะมีความจำเป็น
dvs=$ตัวหาร
เศษเหลือ=1

จนกระทั่ง [ "$remainder" -eq 0 ]
ทำ
ให้ "ส่วนที่เหลือ = เงินปันผล % ตัวหาร"
เงินปันผล=$ตัวหาร
ตัวหาร=$เศษ
เสร็จแล้ว

Echo "GCD ของตัวเลข $dnd และ $dvs = $dividend"


ผลลัพธ์ของการดำเนินการสคริปต์:
ite@ite-เดสก์ท็อป:~$ ./bash2_primer3.sh
ป้อนตัวเศษ:
100
ป้อนตัวส่วน:
90
GCD ของตัวเลข 100 และ 90 = 10

การดำเนินการทางคณิตศาสตร์

ปล่อยให้คำสั่ง
คำสั่งให้ดำเนินการทางคณิตศาสตร์กับตัวเลขและตัวแปร
ลองดูตัวอย่างเล็ก ๆ ที่เราคำนวณตัวเลขที่ป้อน:
#!/bin/bash
echo "ป้อน:"
อ่านก
echo "ป้อน b:"
อ่านข

ให้ "c = a + b" #บวก
เสียงสะท้อน "a+b=$c"
ให้ "c = a/b" #กอง
เสียงสะท้อน "a/b=$c"
ให้ "ค<<= 2" #сдвигает c на 2 разряда влево
echo "c หลังจากกะไป 2 บิต: $c"
ให้ "c = a % b" # หาเศษที่เหลือหารด้วย b
echo "$a / $b. ส่วนที่เหลือ: $c"


ผลการดำเนินการ:
ite@ite-เดสก์ท็อป:~$ ./bash2_primer2.sh
ป้อน:
123
ป้อนข:
12
ก+ข= 135
มี/ข= 10
c หลังจากเปลี่ยน 2 หลัก: 40
123 / 12. ยอดคงเหลือ: 3

อย่างที่คุณเห็นไม่มีอะไรซับซ้อน รายการการดำเนินการทางคณิตศาสตร์เป็นมาตรฐาน:
+ - นอกจากนี้
- - การลบ
* - การคูณ
/ - แผนก
** - การยกกำลัง
% - โมดูลัส (การหารแบบโมดูโล) ส่วนที่เหลือของการหาร
ให้ อนุญาตให้คุณใช้คำย่อสำหรับคำสั่งทางคณิตศาสตร์ ซึ่งจะช่วยลดจำนวนตัวแปรที่ใช้ ตัวอย่างเช่น: a = a+b เทียบเท่ากับ a +=b เป็นต้น

การทำงานกับโปรแกรมภายนอกเมื่อเขียนเชลล์สคริปต์

ประการแรก ทฤษฎีที่เป็นประโยชน์บางประการ
การเปลี่ยนเส้นทางสตรีม
Bash (เช่นเดียวกับเชลล์อื่น ๆ ) มีตัวอธิบายไฟล์ในตัว: 0 (stdin), 1 (stdout), 2 (stderr)
stdout - เอาต์พุตมาตรฐาน ทุกสิ่งที่โปรแกรมส่งออกไปที่นี่
stdin - อินพุตมาตรฐาน นี่คือทั้งหมดที่ผู้ใช้พิมพ์ในคอนโซล
stderr - เอาต์พุตข้อผิดพลาดมาตรฐาน
สำหรับการดำเนินการกับจุดจับเหล่านี้ จะมีอักขระพิเศษ: > (การเปลี่ยนเส้นทางเอาต์พุต)< (перенаправление ввода). Оперировать ими не сложно. Например:
เปลี่ยนเส้นทางเอาต์พุตของคำสั่ง cat /dev/random ไปที่ /dev/null (การดำเนินการที่ไม่มีประโยชน์อย่างแน่นอน :))) หรือ
เขียนเนื้อหาของไดเร็กทอรีปัจจุบันลงในไฟล์รายการ (มีประโยชน์มากกว่า)
หากมีความจำเป็นต้องต่อท้ายไฟล์ (เมื่อใช้ ">" ไฟล์จะถูกแทนที่) คุณต้องใช้ ">>" แทน ">"
หลังจากถามรหัสผ่าน sudo มันจะถูกนำมาจากไฟล์ my_password ราวกับว่าคุณป้อนจากคีย์บอร์ด
หากคุณต้องการเขียนเฉพาะข้อผิดพลาดที่อาจเกิดขึ้นขณะรันโปรแกรมลงในไฟล์ คุณสามารถใช้:
./program_with_error 2> error_file
หมายเลข 2 ก่อน ">" หมายความว่าคุณต้องเปลี่ยนเส้นทางทุกสิ่งที่ลงท้ายด้วย descriptor 2 (stderr)
หากคุณต้องการบังคับให้ stderr เขียนถึง stdout ก็สามารถทำได้ดังนี้ ทาง:
สัญลักษณ์ "&" หมายถึงตัวชี้ไปยัง descriptor 1(stdout)
(ตามค่าเริ่มต้น stderr จะเขียนไปยังคอนโซลที่ผู้ใช้ทำงานอยู่ (หรือจะเขียนไปที่จอแสดงผล))
2. สายพานลำเลียง
ไปป์ไลน์เป็นเครื่องมือที่ทรงพลังมากสำหรับการทำงานกับคอนโซล Bash ไวยากรณ์นั้นง่าย:
ทีม1 | คำสั่ง 2 - หมายความว่าเอาต์พุตของคำสั่ง 1 จะถูกส่งผ่านเป็นอินพุตไปยังคำสั่ง 2
ไปป์ไลน์สามารถจัดกลุ่มเป็นลูกโซ่และส่งออกโดยใช้การเปลี่ยนเส้นทางไปยังไฟล์ ตัวอย่างเช่น:
ls -la | grep "hash" |sort > sortilg_list
เอาต์พุตของคำสั่ง ls -la จะถูกส่งผ่านไปยังคำสั่ง grep ซึ่งเลือกบรรทัดทั้งหมดที่มีคำว่า hash และส่งผ่านไปยังคำสั่ง เรียงลำดับซึ่งเขียนผลลัพธ์ลงในไฟล์ sorting_list ทุกอย่างค่อนข้างชัดเจนและเรียบง่าย

บ่อยครั้งที่สคริปต์ Bash ถูกใช้เพื่อทำให้การดำเนินการรูทีนบางอย่างในคอนโซลเป็นไปโดยอัตโนมัติ ดังนั้นบางครั้งจึงจำเป็นต้องประมวลผล stdout ของคำสั่งหนึ่งและถ่ายโอนไปยัง stdin ไปยังคำสั่งอื่น ในขณะที่ผลลัพธ์ของคำสั่งหนึ่งจะต้องได้รับการประมวลผลด้วยวิธีใดวิธีหนึ่ง ในส่วนนี้ ฉันจะพยายามอธิบายหลักการพื้นฐานของการทำงานด้วย ทีมภายนอกภายในสคริปต์ ฉันคิดว่าฉันได้ยกตัวอย่างมาเพียงพอแล้วและตอนนี้ฉันสามารถเขียนได้เฉพาะประเด็นหลักเท่านั้น

1. การส่งผ่านเอาต์พุตไปยังตัวแปร
ในการบันทึกเอาต์พุตของคำสั่งลงในตัวแปร ก็เพียงพอที่จะใส่คำสั่งไว้ในเครื่องหมายคำพูด `` เป็นต้น
a = `เอคโค "qwerty"`
สะท้อน $a

ผลลัพธ์: qwerty


อย่างไรก็ตาม หากคุณต้องการจัดเก็บรายการไดเร็กทอรีในตัวแปร คุณต้องประมวลผลผลลัพธ์อย่างเหมาะสมเพื่อวางข้อมูลในตัวแปร ลองดูตัวอย่างเล็กๆ น้อยๆ:
LIST=`ค้นหา /svn/ -type d 2>/dev/null| awk "(FS="/") (พิมพ์ $4)"| เรียงลำดับ|uniq | tr "\n" " "`
สำหรับ ONE_OF_LIST ใน $LIST
ทำ
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
เสร็จแล้ว

ที่นี่เราใช้ for-do-done loop เพื่อเก็บถาวรไดเร็กทอรีทั้งหมดในโฟลเดอร์ /svn/ โดยใช้คำสั่ง svnadmin hotcopy (ซึ่งในกรณีของเราไม่สำคัญ เช่นเดียวกับตัวอย่าง) บรรทัดที่น่าสนใจที่สุดคือ: LIST=`find /svn/ -type d 2>/dev/null| awk "(FS="/") (พิมพ์ $4)"| เรียงลำดับ|uniq | tr "\n" " "` มันกำหนดตัวแปร LIST เพื่อดำเนินการ ค้นหาคำสั่งประมวลผลโดยคำสั่ง awk, sort, uniq, tr (เราจะไม่พิจารณาคำสั่งเหล่านี้ทั้งหมด เนื่องจากนี่เป็นบทความแยกต่างหาก) ตัวแปร LIST จะมีชื่อของไดเร็กทอรีทั้งหมดในโฟลเดอร์ /svn/ ที่วางอยู่บนบรรทัดเดียว (เพื่อป้อนเข้าในลูป

อย่างที่คุณเห็นทุกอย่างไม่ยากเพียงแค่เข้าใจหลักการและเขียนสคริปต์ของคุณเองสองสามตัว โดยสรุปของบทความนี้ ฉันขอให้คุณโชคดีในการเรียนรู้ BASH และ Linux โดยทั่วไป ยินดีต้อนรับการวิจารณ์ตามปกติ บทความถัดไปอาจจะเกี่ยวกับการใช้โปรแกรมเช่น sed, awk

หนึ่งในกฎพื้นฐาน การบริหารระบบกล่าวอย่างนี้: หากคุณต้องการทำสิ่งเดียวกันซ้ำแล้วซ้ำเล่า ให้เขียนสคริปต์และปล่อยให้มันทำงานแทนคุณ หากคุณต้องการดำเนินการบางอย่างภายในสคริปต์หลายครั้ง คุณควรใช้ รอบ- ใน GNU ทุบตีคุณสามารถสร้างลูปโดยใช้โครงสร้าง สำหรับ, ในขณะที่และ จนกระทั่ง.

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

วงจร สำหรับได้รับการออกแบบมาเพื่อทำซ้ำการกระทำจนกว่าจะเสร็จสิ้นทั้งหมด ลองนึกภาพว่าคุณมีแคตตาล็อกรูปภาพและคุณจำเป็นต้องแปลงรูปภาพจากรูปแบบหนึ่งไปเป็นอีกรูปแบบหนึ่ง คุณสามารถใช้การวนซ้ำ สำหรับร่วมกับโปรแกรม แปลงจากแพ็คเกจ ImageMagick(หรือโปรแกรมอื่นๆ) เช่น เพื่อแปลงรูปภาพจาก รูปแบบ JPEGเป็นรูปแบบ PNG หรือตัวอย่างเช่น คุณอาจต้องแปลงไฟล์เสียงจำนวนมากจาก เอ็มพี3วี โอจีจี วอร์บิส.

วงจร ในขณะที่ใช้ในการทำซ้ำการกระทำ ลาก่อนถูกดำเนินการ (คือ จริง) เงื่อนไขบางอย่าง วงจร จนกระทั่งทำงานแตกต่างออกไปเล็กน้อย: ดำเนินการ จนกระทั่งถึงตอนนั้นจนกว่าจะตรงตามเงื่อนไข ตัวอย่างเช่น คุณสามารถมีตัวนับและดำเนินการได้ จนกระทั่งถึงตอนนั้นจนกระทั่งค่าถึง 10 ลองดูรายละเอียดเพิ่มเติมโดยใช้ตัวอย่าง

เริ่มจากวงจรกันก่อน สำหรับ- รูปแบบคือ:

สำหรับฉันอยู่ใน $(command); ทำคำสั่ง $i; เสร็จแล้ว

หากคุณกำลังใช้การวนซ้ำ สำหรับในสคริปต์ควรจัดรูปแบบดังนี้:

#!/bin/bash สำหรับฉันใน $(command); ทำคำสั่ง $i เสร็จแล้ว

ตัวอย่างเช่นหากคุณจำเป็นต้องทำ การสำรองข้อมูลไฟล์ HTML ทั้งหมดที่อยู่ในไดเร็กทอรี คุณสามารถใช้คำสั่งต่อไปนี้:

สำหรับฉันใน $(ls *html); ทำ cp $i $i.bak; เสร็จแล้ว

มีการสร้างตัวแปรท้องถิ่นที่นี่ $ฉันคำสั่งจะถูกดำเนินการ ls *htmlผลลัพธ์ที่ได้จะเป็นข้อมูลที่เริ่มต้นค่าของตัวแปร $ฉันในการวนซ้ำแต่ละครั้ง (ในตัวอย่างของเรา นี่จะเป็นรายการไฟล์ที่ส่งคืนโดยคำสั่ง LSหนึ่งรายการสำหรับการวนซ้ำแต่ละครั้ง) จากนั้นคำสั่งจะถูกดำเนินการ ซีพีซึ่งตัวแปรจะถูกส่งผ่านไปยังพารามิเตอร์ต่างๆ $ฉัน.

บางคนอาจถามว่าจำเป็นต้องใช้จดหมายหรือไม่ "ฉัน"เป็นชื่อตัวแปร? เลขที่ คุณสามารถใช้สิ่งที่ถูกต้องได้ ทุบตีชื่อตัวแปร แน่นอนว่า ควรใช้ชื่อตัวแปรที่มีความหมายมากกว่าในสคริปต์ของคุณ เช่น $อินพุตหรือ $html.

ฉันยกตัวอย่างการใช้ลูปที่สั้นและเรียบง่าย สำหรับ- แทนที่จะเป็นคำสั่งที่ดำเนินการในบล็อก ทำ,ใช้ เสียงสะท้อนเพื่อดูพารามิเตอร์ที่ส่งไป นี่เป็นแนวทางปฏิบัติที่มีประโยชน์มากในขั้นตอนการทดสอบสคริปต์เช่นกัน วิธีที่ดีช่วยให้คุณเข้าใจงานได้ละเอียดมากขึ้น สำหรับ.

ในขณะที่และจนกระทั่ง

ให้เราพิจารณาการก่อสร้าง ในขณะที่และ จนกระทั่ง- นอกจากนี้เรายังจะใช้เงื่อนไข bash เล็กน้อย ในตัวอย่างของเรา เราจะใช้ตัวแปรเหล่านี้เพื่อกำหนด เช่น ค่าของตัวแปรมากกว่าหรือน้อยกว่าตัวเลข X ไฟล์มีอยู่และเป็นไดเร็กทอรีหรือไม่ คุณยังสามารถใช้นิพจน์แบบมีเงื่อนไขเพื่อกำหนดว่าไฟล์นั้นสามารถอ่านได้หรือไม่ หรือไฟล์นั้นมีบิต GID อยู่ในสิทธิ์อนุญาตหรือไม่

มาลองทำสิ่งง่ายๆ เช่น การสร้างไฟล์เปล่าๆ สิ่งนี้ไม่น่าจะเป็นประโยชน์กับคุณในชีวิต แต่เป็นตัวอย่าง นี่คือสิ่งนี้

#!/bin/bash i=0 ในขณะที่ [ $i -lt 22 ] แตะที่ $i i=$[$i+1] เสร็จแล้ว

สคริปต์นี้จะสร้างไฟล์ 22 ไฟล์ที่มีชื่อตั้งแต่ 0 ถึง 21 การวนซ้ำจะทำงานจนถึง ลาก่อนค่าตัวแปร $ฉันน้อย ( -lt) 22.

ตอนนี้เรามากำจัดไฟล์ที่สร้างขึ้นโดยใช้การวนซ้ำ จนกระทั่ง:

#!/bin/bash i=0 จนถึง [ $i -eq 22 ] do rm $i i=$[$i+1] เสร็จแล้ว

ที่นี่เราได้แทนที่ ในขณะที่บน จนกระทั่งและในนิพจน์เงื่อนไขที่พวกเขาแทนที่ "น้อย" (-lt) บน "เท่ากับ" (-eq- ดังนั้นสคริปต์ของเราจะทำงานตราบเท่าที่ค่านั้น $ฉันจะไม่ถึง 22. และแทน สัมผัสเราใช้ RMเพื่อลบไฟล์แทนที่จะสร้างมันขึ้นมา ง่ายใช่มั้ย?

สำหรับวีเออาร์ ใน 1 2 3...น ทำ เสร็จแล้วหรือในหนึ่งบรรทัด: สำหรับวีเออาร์ ใน 1 2 3...น; ทำ ; เสร็จแล้ว
สามารถแทนที่ทั้งค่าตัวเลขและอักขระ ASCII ลงในตัวแปรได้
ตัวอย่าง: $ สำหรับ i ใน 1 2 A B Abc ; ทำเสียงสะท้อน $i; เสร็จสิ้น 1 2 A B Abc ตัวอย่างการแสดงรายการไฟล์ในตัวแปรด้วย “mask” สำหรับการแปลงรหัสวิดีโอ: สำหรับฉัน ใน*.avi; ทำ ; เสร็จแล้ว

2. การแทนที่ผลลัพธ์ของคำสั่งอื่น

สำหรับวีเออาร์ ใน $(); ทำ ; เสร็จแล้ว
ตัวอย่างการใช้ผลลัพธ์ของคำสั่ง seq: สำหรับฉัน ใน$(seq [คีย์]); ทำเสียงสะท้อน $i; เสร็จแล้ว$ สำหรับฉันใน $(seq 3); ทำเสียงสะท้อน $i; ทำ 1 2 3 $ สำหรับฉันใน $(seq 3 5); ทำเสียงสะท้อน $i; ทำ 3 4 5 $ สำหรับฉันใน $(seq 2 2 6); ทำเสียงสะท้อน $i; เสร็จสิ้น 2 4 6 ตัวอย่างการใช้ผลลัพธ์ของคำสั่ง ls: $ for i in $(ls /$HOME/Video); ทำเสียงสะท้อน $i; เสร็จแล้ว 001.avi 002.avi 003.avi

3. การทดแทนโดยใช้สไตล์ C (สไตล์ C)

สำหรับ((EXPR1; EXPR2; EXPR3)) ทำ <список команд> เสร็จแล้ว สำหรับ((i=1; ผม<=3 ; i++)); ทำเสียงสะท้อน $i; เสร็จแล้ว$ สำหรับ ((i=1; i<=3 ; i++)); do echo $i; done 1 2 3 Подробнее о применении C-style в Bash

4. การแจงนับโดยใช้เครื่องหมายปีกกา (..)

รองรับไวยากรณ์ (START...END) โดยเริ่มด้วย bash เวอร์ชัน 3.0+ และไวยากรณ์ (START..END..INCREMENT) ได้รับการสนับสนุนโดยเริ่มด้วย bash เวอร์ชัน 4.0+:

สำหรับวีเออาร์ ใน {..} ทำ เสร็จแล้วหรือ สำหรับวีเออาร์ ใน {....} ทำ เสร็จแล้วตัวอย่าง: $ สำหรับฉันใน (1..3); ทำเสียงสะท้อน $i; ทำ 1 2 3 หรือ $ สำหรับฉันใน (4..8..2); ทำเสียงสะท้อน $i; เสร็จสิ้น 4 6 8 การนับสามารถทำได้ทั้งการเพิ่มและลดค่า: $ for i in (6..-4..3); ทำเสียงสะท้อน $i; เสร็จแล้ว 6 3 0 -3

5. การทดแทนพารามิเตอร์ ( ใน "$@")

ดำเนินการคำสั่งสำหรับแต่ละพารามิเตอร์ที่ส่งไปยังสคริปต์ สำหรับวีเออาร์ ใน $@ ทำ เสร็จแล้วหรือในหนึ่งบรรทัด: สำหรับวีเออาร์ ใน $@; ทำ ; เสร็จแล้ว
ดังนั้นหากคุณสร้างสคริปต์ test.sh #!/bin/sh สำหรับวีเออาร์ ใน $@ ทำ เสียงสะท้อน$วาร์ เสร็จแล้วจากนั้นเมื่อคุณรันด้วยพารามิเตอร์: $ ./test.sh param1 param2 param3 param1 param2 param3 Part ใน$@ สามารถละเว้นได้ จากนั้นสคริปต์ test.sh จะถูกเขียนใหม่: #!/bin/sh สำหรับวีเออาร์ ทำ เสียงสะท้อน$วาร์ เสร็จแล้ว
ฉันขอยกตัวอย่างสองสามตัวอย่าง (ด้วย ในและไม่มี): $ function FUNC_1 ( สำหรับ VAR ใน $@; do echo $VAR; เสร็จสิ้น; ) $ FUNC_1 param1 param2 param3 param1 param2 param3 $ function FUNC_2 ( สำหรับ VAR; do echo $VAR; เสร็จสิ้น; ) $ FUNC_2 param1 param2 param3 พารามิเตอร์ 1 พารามิเตอร์ 2 พารามิเตอร์ 3

6. ใช้ Continue และแบ่ง For Loop

สำหรับโครงสร้างข้างต้นทั้งหมด คุณสามารถใช้คำสั่ง "ดำเนินการต่อ" เพื่อย้ายไปยังองค์ประกอบถัดไปของลูป หรือ "หยุด" เพื่อออกจากลูป

ตัวอย่าง (สมบูรณ์เมื่อ i=6 และไม่ต้องดำเนินการเมื่อ i=3 และ i=5): สำหรับฉันอยู่ใน (1..8); ทำ ถ้า[ $i -eq 6 ]; แล้วหยุดพัก; ฟิ ถ้า[ $i -eq 3 ] || [ $i -eq 5 ]; แล้วดำเนินการต่อ; fi echo $i เสร็จแล้วผลการดำเนินการ: $ $ สำหรับฉันใน (1..8); ทำ \ > ถ้า [ $i -eq 6 ]; แล้วแตก; ฟี; \ > ถ้า [ $i -eq 3 ] || [ $i -eq 5 ]; จากนั้นดำเนินการต่อ; ฟี; \ > เสียงสะท้อน $i; \ > เสร็จแล้ว 1 2 4

คำอธิบายสั้น ๆ เกี่ยวกับความแตกต่างในประเภทลูป:

สำหรับ - จะดำเนินการตราบเท่าที่มีวัตถุที่จะดำเนินการ (เช่นการอ่านสตรีมจาก stdin ไฟล์หรือฟังก์ชัน)
ในขณะที่ - ดำเนินการจนกระทั่ง เงื่อนไขเป็นความจริง;
จนกระทั่ง - จะถูกดำเนินการตราบเท่าที่ เงื่อนไขจะไม่เป็นจริงเช่น สำหรับตอนนี้มันเป็นเท็จ

สำหรับห่วง

พิจารณาสคริปต์เวอร์ชันนี้แบบวนซ้ำ:

$ cat loop.sh #!/bin/bash สำหรับตัวแปรใน `ls -1` ทำ echo "$variable" เสร็จแล้ว

ไวยากรณ์นั้นง่ายมากและแสดงให้เห็นอย่างชัดเจนในตัวอย่าง:

สำหรับ (เริ่มลูป) ตัวแปร (ประกาศตัวแปรที่เราจะดำเนินการ) ใน (ส่งโฟลว์ไปยังลูป) `ls -1` (คำสั่งที่จะถูกดำเนินการและส่งผ่านไปยังตัวแปร $variable) สิ่งที่ทำและทำเสร็จแล้วคือ "เนื้อหา" ของลูป ซึ่งภายในการดำเนินการหลักจะดำเนินการกับข้อมูลที่ได้รับ และเสียงสะท้อน "$variable" คือการกระทำจริงที่ดำเนินการโดยลูป

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

$ cat loop.sh #!/bin/bash ls=`ls -1` สำหรับตัวแปรใน $ls ทำ echo "$variable" เสร็จแล้ว

ตอนนี้คำสั่ง ls -1 ถูกส่งผ่านในตัวแปรแยกต่างหาก ซึ่งช่วยให้คุณทำงานกับลูปได้อย่างยืดหยุ่นมากขึ้น แทนที่จะใช้ตัวแปรในลูป คุณยังสามารถใช้ฟังก์ชันได้:

$ cat loop.sh #!/bin/bash lsl () ( ls -1 ) สำหรับตัวแปรใน `lsl` ทำ echo "$variable" เสร็จแล้ว

เงื่อนไขหลักของ for loop คือมันจะถูกดำเนินการตราบใดที่คำสั่งที่ส่งไปนั้นมีออบเจ็กต์สำหรับการดำเนินการ ตามตัวอย่างด้านบน - ตราบใดที่ ls -1 มีไฟล์ที่จะแสดง - ลูปจะส่งผ่านไฟล์เหล่านั้นไปยังตัวแปรและดำเนินการ "loop body" ทันทีที่รายการไฟล์ในไดเร็กทอรีสิ้นสุดลง วงจรจะดำเนินการเสร็จสิ้น

มาทำให้ตัวอย่างซับซ้อนขึ้นอีกหน่อย

ไดเรกทอรีประกอบด้วยรายการไฟล์:

$ ls -1 file1 file2 file3 file4 file5 loop.sh nofile1 nofile2 nofile3 nofile4 nofile5

เราต้องเลือกจากพวกเขาเฉพาะที่ไม่มีคำว่า " เลขที่«:

$ cat loop.sh #!/bin/bash lsl=`ls -1` สำหรับตัวแปรใน $lsl do echo "$variable" | grep -v "ไม่" เสร็จแล้ว $ ./loop.sh file1 file2 file3 file4 file5 loop.sh

คุณยังสามารถใช้นิพจน์แบบมีเงื่อนไขในลูป ( การแสดงออกตามเงื่อนไข) […] เพื่อตรวจสอบเงื่อนไขและคำสั่งแบ่งเพื่อขัดจังหวะลูปหากเงื่อนไขถูกทริกเกอร์

ลองพิจารณาตัวอย่างนี้:

$ cat loop.sh #!/bin/bash lsl=`ls -1` สำหรับตัวแปรใน $lsl do if [ $variable != "loop.sh" ] จากนั้น echo "$variable" | grep -v "no" อย่างอื่นพังเสร็จเรียบร้อย

การวนซ้ำจะดำเนินต่อไปจนกว่าจะพบไฟล์ loop.sh ทันทีที่การดำเนินการของลูปมาถึงไฟล์นี้ ลูปจะถูกขัดจังหวะด้วยคำสั่ง break:

$ ./loop.sh ไฟล์1 ไฟล์2 ไฟล์3 ไฟล์4 ไฟล์5

อีกตัวอย่างหนึ่งคือการใช้การดำเนินการทางคณิตศาสตร์ทันทีก่อนดำเนินการเนื้อความของลูป:

$ cat loop.sh #!/bin/bash สำหรับ ((count=1; count<11; count++)) do echo "$count" done

ที่นี่เราตั้งค่าคำสั่งควบคุมสามคำสั่ง - count=1, เงื่อนไขการควบคุม - ในขณะที่จำนวนน้อยกว่า 11 และคำสั่งให้ดำเนินการ - count +1:

WHILE และ UNTIL วนซ้ำ

ตัวอย่างง่ายๆ ที่แสดงให้เห็นอย่างชัดเจนว่า while loop ทำงานอย่างไร:

$ cat loop.sh #!/bin/bash count=0 ในขณะที่ [ $count -lt 10 ] do ((count++)) echo $count เสร็จแล้ว

เราตั้งค่าตัวแปร $count ให้เป็นศูนย์ จากนั้นรันคำสั่ง whi le โดยมีเงื่อนไข “ในขณะที่ $count น้อยกว่าสิบ ให้ดำเนินการลูป” ในเนื้อความของลูปที่เราดำเนินการ การเพิ่มขึ้นภายหลัง+1 ให้กับตัวแปร $count และผลลัพธ์จะถูกพิมพ์ไปที่ stdout

ผลการดำเนินการ:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10

ทันทีที่ค่าของตัวแปร $count กลายเป็น 10 การวนซ้ำก็หยุดลง

ตัวอย่างที่ดีของการวนซ้ำแบบ "ไม่มีที่สิ้นสุด" ที่สาธิตวิธีการทำงานของ while:

$ cat loop.sh #!/bin/bash count=10 ในขณะที่ [ 1 = 1 ] ทำ ((นับ++)) echo $count เสร็จแล้ว $ ./loop.sh ... 5378 5379 5380 5381 5382 5383 ^C

การวนซ้ำจนกระทั่งทำงานในลักษณะเดียวกัน แต่ไปในทิศทางตรงกันข้าม:

$ cat loop.sh #!/bin/bash count=0 จนถึง [ $count -gt 10 ] do ((count++)) echo $count เสร็จสิ้น

ที่นี่เราตั้งค่าเงื่อนไขที่คล้ายกัน แต่แทนที่จะระบุ "ในขณะที่ตัวแปรน้อยกว่า 10" เราระบุ "จนกว่าตัวแปรจะมีค่ามากกว่า 10" ผลการดำเนินการ:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10 11

หากตัวอย่างข้างต้นของ “endless loop” ถูกดำเนินการโดยใช้ until มันจะไม่ส่งออกสิ่งใด ซึ่งแตกต่างจาก while:

$ cat loop.sh #!/bin/bash count=10 จนถึง [ 1 = 1 ] do ((count++)) echo $count เสร็จแล้ว $ ./loop.sh $

เพราะ " เงื่อนไข"เดิมที" จริง"—เนื้อความของลูปจะไม่ถูกดำเนินการ

เช่นเดียวกับใน for loop คุณสามารถใช้ฟังก์ชันใน while และ until ได้ ตัวอย่างเช่น การวนซ้ำจากสคริปต์ในชีวิตจริงที่ตรวจสอบสถานะของเซิร์ฟเวอร์ แมวตัวผู้(PID ถูกนำมาจากระบบ สลสอาจแตกต่างกันในระบบอื่น) เวอร์ชันที่เรียบง่ายเล็กน้อย:

$ cat loop.sh #!/bin/bash check_tomcat_status () ( RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "(พิมพ์ $2)"` ) ในขณะที่ check_tomcat_status ทำถ้า [ -n "$ RUN" ] จากนั้น printf "คำเตือน: Tomcat ยังคงทำงานโดยมี PID $RUN"

ผลการดำเนินการ:

$ ./loop.sh คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคง ทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435 26548 คำเตือน: Tomcat ยังคงทำงานด้วย PID 14435

เวอร์ชันเต็ม:

Check_tomcat_status () ( RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "(พิมพ์ $2)"` ) ในขณะที่ check_tomcat_status; จะทำอย่างไรถ้า [ -n "$RUN" ] จากนั้น printf "คำเตือน: Tomcat ยังคงทำงานด้วย PID $RUN หยุดไหม " ตอบ "หยุด Tomcat..." "กำลังดำเนินการติดตั้ง..." && $CATALINA_HOME/bin/shutdown . sh 2&>1 /dev/null || พัก sleep 2 ถ้า [ -n "$RUN" ] แล้ว printf "Tomcat ยังคงทำงานอยู่ ฆ่ามันเหรอ?" ตอบ "กำลังฆ่า Tomcat..." "กำลังดำเนินการติดตั้ง...n" && kill $RUN || พักนอน 2 อย่างอื่น printf "Tomcat หยุดแล้วดำเนินการต่อ ... nn" พักเสร็จเรียบร้อยแล้ว

ฟังก์ชันคำตอบได้อธิบายไว้ในบทความ แต่มีการใช้เวอร์ชันที่ได้รับการปรับปรุงเล็กน้อย:

คำตอบ () ( ในขณะที่อ่านคำตอบ ทำ echo case $response ใน |) printf "$1n" return 0 break ;;

|) printf "$2n" คืน 1 ตัวแบ่ง ;;