วิธีการวนซ้ำ n ครั้งใน Bash


9

ฉันมีสถานการณ์ด้านล่างเช่น:

if [file exists]; then
   exit   
elif
   recheck if file exist (max 10 times)
   if found exit else recheck again as per counter  
fi 

นี่เป็นคุณสมบัติพื้นฐานในเชลล์ คุณค้นคว้าด้วยซ้ำหรือไม่?
Peschke

ใช่. แต่ไม่ได้รับผลลัพธ์ที่คาดหวังจากรหัสของฉัน และต้องการเขียนย่อให้มากที่สุด
Rocky86

1
@Peschke ดีพวกเขาต้องการอย่างน้อยสามคุณสมบัติพื้นฐาน (ลูป, เงื่อนไข, ทดสอบไฟล์, แยกออกจากวง) อย่างน้อยคำถามก็ค่อนข้างชัดเจนเหมือนเดิม แม้ว่ามันจะมีภาพร่างของสิ่งที่ร็อคกี้พยายาม แต่ก็มีบางคนเขียนมันใหม่ในคำตอบต่อไป ;)
ilkkachu

คำตอบ:


9

การวนซ้ำนี้ทำได้หลายวิธี

ด้วยksh93ไวยากรณ์ (สนับสนุนโดยzshและbash):

for (( i=0; i<10; ++i)); do
    [ -e filename ] && break
    sleep 10
done

สำหรับเชลล์ที่มีลักษณะคล้าย POSIX:

n=0
while [ "$n" -lt 10 ] && [ ! -e filename ]; do
    n=$(( n + 1 ))
    sleep 10
done

ลูปทั้งคู่พัก 10 วินาทีในการวนซ้ำแต่ละครั้งก่อนทดสอบการมีอยู่ของไฟล์อีกครั้ง

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

หากคุณต้องการและหากคุณสามารถเข้าถึง inotify-tools คุณสามารถแทนที่การsleep 10โทรด้วย

inotifywait -q -t 10 -e create ./ >/dev/null

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

รหัสเต็มด้วยinotifywait(แทนที่ด้วยsleep 10หากคุณไม่ต้องการ) อาจมีลักษณะเช่นนั้น

for (( i=0; i<10; ++i)); do
    [ -e filename ] && break
    inotifywait -q -t 10 -e create ./ >/dev/null
done

if [ -e filename ]; then
    echo 'file appeared!'
else
    echo 'file did not turn up in time'
fi

ด้วย inotify คุณสามารถแทนที่ลูปทั้งหมดได้ เพียงทดสอบว่ามีไฟล์อยู่หรือไม่หากไม่ทราบจะรอ 100 วินาที เกือบเนื่องจากไฟล์จะถูกสร้างขึ้นเพียงระหว่างการทดสอบและการ inotify และคุณจะนอนหลับได้เต็ม 100 วินาทีก่อนที่จะหมดเวลา ...
ilkkachu

1
@ilkkachu ใช่ว่าเป็นความคิดที่ดี แต่ที่นี่ผมเพียงแค่ใช้เป็นแบบเลื่อนแทนinotifywait sleep
Kusalananda

8

หากการนับไม่ใช่ตัวแปรคุณสามารถใช้การขยายรั้ง:

for i in {1..10}   # you can also use {0..9}
do
  whatever
done

หากการนับเป็นตัวแปรคุณสามารถใช้seqคำสั่ง:

count=10
for i in $(seq $count)
do
  whatever
done

ฉันต้องการวนลูปเฉพาะเมื่อไม่พบไฟล์ (สูงสุด 10 ครั้ง) หากพบว่าให้บอกครั้งที่ 3 ให้ออกสำเร็จ
Rocky86

@ Rocky86: นี่ไม่ได้ขัดแย้งกับโซลูชั่นที่เสนอโดย xenoid ไม่มีใครบังคับให้คุณนับจนกว่าจะหมด ....
user1934428

ฉันเช่นนี้$(seq $count)
คนทำงาน

0
n=0
until [ "$((n+=1))" -gt 10 ]
do    <exists? command exit
done
echo oh noes!

แม้ว่าtest -e file && exitจะมีความยืดหยุ่นมากกว่า


ทำไมเครื่องหมายคำถาม โปรดทราบว่าพฤติกรรมสำหรับ globs ในเป้าหมายของการเปลี่ยนเส้นทางจะแตกต่างกันระหว่างเชลล์
Stéphane Chazelas

2
โปรดทราบว่ามันมีผลข้างเคียงของการเปิดไฟล์ซึ่งสำหรับ fifos เช่นนั้นค่อนข้างแย่ (แย่กว่านั้นด้วย symlink to / dev / watchdog บน Linux เป็นต้น)
Stéphane Chazelas

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

@ikkachu - ใช่ นั่นคือจุดที่ หากข้อผิดพลาดเกิดขึ้นสคริปต์รายงาน ถ้า stderr done 2<>/dev/nullควรจะระงับปราบปราม ไม่bashไม่ว่าสคริปต์? ฉันคิดว่ามันจะทำให้เกิดขึ้นใน-iบริบท nteractive ยังคงเป็นมากเป็นชื่อฟิลเลอร์เป็นexists? fileแต่ใช่ฉันเกลียดอ้างในการเปลี่ยนเส้นทาง - ถ้าสกรูเพื่อให้ มากขึ้น
mikeserv

@ Stéphane - ไม่มีเหตุผลจริงๆ แต่ใช่ FIFOs, unreadables ... thats test -eทำไมผมตั้งข้อสังเกต
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.