Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> การเขียนโปรแกรม BASH

วิธีเขียนลูปใน Bash

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

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

คลาสสิกสำหรับลูป

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

$ ตัวอย่าง mkdir
        $ cp ~/Pictures/vacation/*.{png,jpg} ตัวอย่าง

เปลี่ยนไดเร็กทอรีเป็นโฟลเดอร์ใหม่ของคุณ จากนั้นแสดงรายการไฟล์ในนั้นเพื่อยืนยันว่าสภาพแวดล้อมการทดสอบของคุณเป็นไปตามที่คุณคาดหวัง:

$ ตัวอย่างซีดี
$ ls -1
cat.jpg
design_maori.png
otago.jpg
waterfall.png

ไวยากรณ์ที่จะวนซ้ำในแต่ละไฟล์ในลูปคือ:สร้างตัวแปร (f สำหรับไฟล์ เป็นต้น) จากนั้นกำหนดชุดข้อมูลที่คุณต้องการให้ตัวแปรหมุนเวียน ในกรณีนี้ วนรอบไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบันโดยใช้ * อักขระตัวแทน (* สัญลักษณ์แทนตรงกับ ทุกอย่าง ). จากนั้นปิดประโยคเกริ่นนำด้วยเครื่องหมายอัฒภาค (; )

$ for f in * ; 

ขึ้นอยู่กับความชอบของคุณ คุณสามารถเลือกที่จะกด ย้อนกลับ ที่นี่. เชลล์จะไม่พยายามดำเนินการวนซ้ำจนกว่าจะเสร็จสิ้นทางวากยสัมพันธ์

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

do file $f ; 

ยุติประโยคด้วยเซมิโคลอนอื่นและปิดลูป:

done 

กด กลับ เพื่อเริ่มต้นเปลือกหมุนผ่าน ทุกอย่าง ในไดเร็กทอรีปัจจุบัน สำหรับ loop กำหนดแต่ละไฟล์ให้กับตัวแปร f . ทีละไฟล์ และรันคำสั่งของคุณ:

$ สำหรับ f ใน *; ทำ
       > file $f;
       > done
        cat.jpg:ข้อมูลภาพ JPEG, EXIF ​​standard 2.2
        design_maori.png:ข้อมูลภาพ PNG, 4608 x 2592, 8 บิต /color RGB, ไม่อินเทอร์เลซ
        otago.jpg:ข้อมูลภาพ JPEG, มาตรฐาน EXIF ​​​​2.2
        waterfall.png:ข้อมูลภาพ PNG, 4608 x 2592, RGB 8 บิต/สี, ไม่อินเทอร์เลซ

คุณยังสามารถเขียนแบบนี้:

$ สำหรับ f ใน *; ทำไฟล์ $f; เสร็จสิ้น
        cat.jpg:ข้อมูลภาพ JPEG, มาตรฐาน EXIF ​​​​2.2
        design_maori.png:ข้อมูลภาพ PNG, 4608 x 2592, RGB 8 บิต/สี, ไม่อินเทอร์เลซ
        otago.jpg:ข้อมูลภาพ JPEG, มาตรฐาน EXIF ​​​​2.2
        waterfall.png:ข้อมูลภาพ PNG, 4608 x 2592, RGB 8 บิต/สี, ไม่อินเทอร์เลซ

ทั้งรูปแบบหลายบรรทัดและบรรทัดเดียวเหมือนกันกับเชลล์ของคุณและให้ผลลัพธ์เหมือนกันทุกประการ

ตัวอย่างที่ใช้งานได้จริง

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

ขั้นแรก ติดตั้ง ImageMagick คำสั่งโดยใช้ตัวจัดการแพ็คเกจของคุณบน Linux, BSD หรือ Mac ตัวอย่างเช่น บน Fedora และ RHEL:

$ sudo dnf install ImageMagick 

บน Ubuntu หรือ Debian:

$ sudo apt install ImageMagick 

บน BSD ให้ใช้ พอร์ต หรือ pkgsrc. บน Mac ให้ใช้ Homebrew หรือ MacPorts

เมื่อคุณติดตั้ง ImageMagick แล้ว คุณจะมีชุดคำสั่งใหม่เพื่อใช้กับรูปภาพ

สร้างไดเร็กทอรีปลายทางสำหรับไฟล์ที่คุณกำลังจะสร้าง:

$ mkdir tmp 

หากต้องการลดขนาดรูปภาพแต่ละรูปให้เหลือ 33% ของขนาดดั้งเดิม ให้ลองใช้ลูปนี้:

$ for f in * ; do convert $f -scale 33% tmp/$f ; done 

จากนั้นดูใน tmp โฟลเดอร์เพื่อดูรูปภาพที่ปรับขนาดของคุณ

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

$ สำหรับ f ใน *; ทำ 
    แปลง $f -scale 33% tmp/$f
    scp -i seth_web tmp/$f seth@example.com:~/public_html
    ถังขยะ tmp/$f;
  เสร็จแล้ว

สำหรับแต่ละไฟล์ที่ประมวลผลโดย สำหรับ วนซ้ำ คอมพิวเตอร์ของคุณจะรันคำสั่งสามคำสั่งโดยอัตโนมัติ ซึ่งหมายความว่าหากคุณประมวลผลเพียง 10 ภาพด้วยวิธีนี้ คุณจะประหยัดเวลาได้ 30 คำสั่งและอย่างน้อยก็อาจนานหลายนาที

จำกัดวงของคุณ

ลูปไม่จำเป็นต้องดูทุกไฟล์เสมอไป คุณอาจต้องการประมวลผลเฉพาะไฟล์ JPEG ในไดเรกทอรีตัวอย่างของคุณ:

$ สำหรับ f ใน *.jpg; ทำแปลง $f -scale 33% tmp/$f; เสร็จสิ้น
$ ls -m tmp
cat.jpg, otago.jpg

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

$ สำหรับ n ใน {0..4}; ทำ echo $n; เสร็จเรียบร้อย
0
1
2
3
4

การวนซ้ำเพิ่มเติม

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

สำหรับขั้นสูง สำหรับ วนซ้ำหัวข้อ อ่านต่อ

ไม่ใช่ทุกเชลล์ที่เป็น Bash

สำหรับ คีย์เวิร์ดถูกสร้างขึ้นใน Bash shell เชลล์ที่คล้ายกันจำนวนมากใช้คำหลักและไวยากรณ์เดียวกัน แต่เชลล์บางตัว เช่น tcsh ใช้คำหลักอื่น เช่น foreach , แทน.

ใน tcsh ไวยากรณ์มีความคล้ายคลึงกัน แต่เข้มงวดกว่า Bash ในตัวอย่างโค้ดต่อไปนี้ อย่าพิมพ์สตริง foreach? ในบรรทัดที่ 2 และ 3 เป็นพรอมต์รองเพื่อเตือนคุณว่าคุณยังอยู่ในขั้นตอนการสร้างลูปของคุณ

$ foreach f (*)
foreach? ไฟล์ $f
foreach? จบ
cat.jpg:ข้อมูลภาพ JPEG, EXIF ​​​​มาตรฐาน 2.2
design_maori.png:ข้อมูลภาพ PNG, 4608 x 2592, RGB 8 บิต/สี, ไม่อินเทอร์เลซ
otago.jpg:ข้อมูลภาพ JPEG, มาตรฐาน EXIF ​​​​2.2
waterfall.png:ข้อมูลภาพ PNG, 4608 x 2592, RGB 8 บิต/สี, ไม่ใช่แบบอินเทอร์เลซ

ใน tcsh ทั้ง foreach และ จบ ต้องปรากฏตามลำพังในบรรทัดที่แยกจากกัน ดังนั้นคุณจึงไม่สามารถสร้าง สำหรับ วนซ้ำในหนึ่งบรรทัดเท่าที่จะทำได้ด้วย Bash และเชลล์ที่คล้ายกัน

สำหรับลูปที่มีคำสั่ง find

ตามทฤษฎี คุณจะพบเชลล์ที่ไม่มี สำหรับ ฟังก์ชันวนซ้ำ หรือคุณอาจต้องการใช้คำสั่งอื่นที่มีคุณลักษณะเพิ่มเติม

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

ค้นหา คำสั่งมีขึ้นเพื่อช่วยคุณค้นหาไฟล์ในฮาร์ดไดรฟ์ของคุณ ไวยากรณ์นั้นเรียบง่าย:คุณระบุเส้นทางของตำแหน่งที่คุณต้องการค้นหา และค้นหา ค้นหาไฟล์และไดเร็กทอรีทั้งหมด:

$ find . 
.
./cat.jpg
./design_maori.png
./otago.jpg
./waterfall.png

คุณสามารถกรองผลการค้นหาโดยเพิ่มบางส่วนของชื่อ:

$ find . -name "*jpg"
./cat.jpg
./otago.jpg

สิ่งที่ยอดเยี่ยมเกี่ยวกับ ค้นหา คือแต่ละไฟล์ที่พบสามารถป้อนเข้าลูปโดยใช้ -exec ธง. ตัวอย่างเช่น หากต้องการลดขนาดเฉพาะรูปภาพ PNG ในไดเรกทอรีตัวอย่างของคุณ:

$ find . -name "*png" -exec แปลง {} -scale 33% tmp/{} \;
$ ls -m tmp
design_maori.png, waterfall.png

ใน -exec ข้อ อักขระวงเล็บ {} ยืนหยัดในสิ่งที่ ค้นหา กำลังประมวลผล (กล่าวคือ ไฟล์ใดๆ ที่ลงท้ายด้วย PNG ที่ตั้งอยู่ ทีละไฟล์) -exec ประโยคจะต้องสิ้นสุดด้วยเครื่องหมายอัฒภาค แต่ Bash มักจะพยายามใช้เครื่องหมายอัฒภาคสำหรับตัวเอง คุณ "หลบหนี" อัฒภาคด้วยแบ็กสแลช (\; ) เพื่อให้ ค้นหา รู้ที่จะปฏิบัติต่ออัฒภาคนั้นเป็นอักขระที่สิ้นสุด

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

$ find . -name "*png" -exec แปลง {} -flip -flop tmp/{} \; 
แปลง:ไม่สามารถเปิดภาพ `tmp/./tmp/design_maori.png':
ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว @ error/blob.c/OpenBlob/2643.
...

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

หากต้องการค้นหาเฉพาะไฟล์ PNG ในไดเรกทอรีปัจจุบัน (ไม่รวมไดเรกทอรีย่อย):

$ find . -maxdepth 1 -name "*png" 

หากต้องการค้นหาและประมวลผลไฟล์ในไดเร็กทอรีปัจจุบันบวกกับระดับของไดเร็กทอรีย่อยเพิ่มเติม ให้เพิ่มความลึกสูงสุด 1 :

$ find . -maxdepth 2 -name "*png" 

ค่าเริ่มต้นคือการลงไปในไดเรกทอรีย่อยทั้งหมด

วนซ้ำเพื่อความสนุกและผลกำไร

ยิ่งคุณใช้ลูปมากเท่าไหร่ คุณก็ยิ่งประหยัดเวลาและความพยายามมากขึ้นเท่านั้น และงานที่คุณจัดการได้ก็จะยิ่งใหญ่ขึ้น คุณเป็นผู้ใช้เพียงรายเดียว แต่ด้วยการใช้วงจรที่รอบคอบ คุณทำให้คอมพิวเตอร์ทำงานหนักได้

คุณสามารถและควรปฏิบัติต่อลูปเหมือนคำสั่งอื่นๆ โดยทำให้มันอยู่ใกล้แค่เอื้อมเมื่อคุณต้องการทำซ้ำการกระทำเดียวหรือสองครั้งในหลายไฟล์ อย่างไรก็ตาม มันยังเป็นประตูสู่การเขียนโปรแกรมที่จริงจัง ดังนั้นหากคุณต้องทำงานที่ซับซ้อนให้สำเร็จในไฟล์จำนวนเท่าใดก็ได้ ใช้เวลาสักครู่ในการวางแผนเวิร์กโฟลว์ของคุณ หากคุณบรรลุเป้าหมายในไฟล์เดียว ให้รวมกระบวนการที่ทำซ้ำนั้นไว้ใน สำหรับ วนซ้ำค่อนข้างง่ายและ "การเขียนโปรแกรม" เพียงอย่างเดียวที่จำเป็นคือการทำความเข้าใจว่าตัวแปรทำงานอย่างไรและองค์กรมากพอที่จะแยกไฟล์ที่ยังไม่ได้ประมวลผลออกจากไฟล์ที่ประมวลผล ด้วยการฝึกฝนเพียงเล็กน้อย คุณสามารถย้ายจากผู้ใช้ Linux ไปเป็นผู้ใช้ Linux ที่รู้วิธีเขียนลูป ดังนั้นออกไปและทำให้คอมพิวเตอร์ของคุณทำงานแทนคุณ!