สำหรับ vs find in Bash


28

เมื่อวนลูปผ่านไฟล์มีสองวิธี:

  1. ใช้for-loop:

    for f in *; do
        echo "$f"
    done
  2. ใช้find:

    find * -prune | while read f; do 
        echo "$f"
    done

สมมติว่าทั้งสองจะได้พบกับลูปในรายการเดียวกันของไฟล์สิ่งที่มีความแตกต่างในทั้งสองตัวเลือกในperfomanceและการจัดการ?


1
ทำไม? findไม่เปิดไฟล์ที่พบ สิ่งเดียวที่ฉันสามารถดูกัดคุณมาที่นี่ด้วยความเคารพเป็นจำนวนมากของไฟล์ที่เป็นARG_MAX
kojiro

1
ดูคำตอบและความคิดเห็นที่บอกคุณว่าread fจะทำให้ชื่อไฟล์ยุ่งเหยิงในขณะที่อ่าน (เช่นชื่อที่มีช่องว่างนำหน้า) นอกจากนี้ยังfind * -pruneน่าจะเป็นวิธีที่ซับซ้อนมากที่จะพูดก็ls -1ใช่?
Ian D. Allen

4
อย่าสันนิษฐานว่าทั้งสองลูปจะพบไฟล์ชุดเดียวกัน ในกรณีส่วนใหญ่พวกเขาจะไม่ นอกจากนี้ที่ควรจะเป็นไม่ได้find . find *
alexis

1
@terdon ใช่การแยกวิเคราะห์ls -lเป็นความคิดที่ไม่ดี แต่แยกls -1(ที่เป็น1ไม่ได้l) find * -pruneไม่เลวร้ายยิ่งกว่าการแยก ทั้งสองล้มเหลวในไฟล์ที่มีบรรทัดใหม่ในชื่อ
Ian D. Allen

5
ฉันสงสัยว่าเราแต่ละคนใช้เวลาอ่านคำถามและคำตอบนี้มากกว่าความแตกต่างในการทำงานตลอดชีวิตของสคริปต์ที่มีปัญหา
mpez0

คำตอบ:


9

1

คนแรก:

for f in *; do
  echo "$f"
done

ล้มเหลวสำหรับไฟล์ที่เรียกว่า-n, -eและสายพันธุ์เช่น-neneและมีการใช้งานบางทุบตีด้วยชื่อไฟล์ที่มีเครื่องหมาย

ที่สอง:

find * -prune | while read f; do 
  echo "$f"
done

ล้มเหลวสำหรับกรณีมากยิ่งขึ้น (แฟ้มที่เรียกว่า!, -H, -name, (, ชื่อไฟล์ที่ขึ้นต้นหรือลงท้ายด้วยช่องว่างหรือมีตัวอักษรขึ้นบรรทัดใหม่ ... )

มันเป็นเปลือกที่ขยาย*, findไม่ทำอะไรเลย แต่พิมพ์ไฟล์ที่ได้รับเป็นข้อโต้แย้ง คุณสามารถใช้printf '%s\n'แทนซึ่งเป็นprintfbuiltin ก็จะหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นargs มากเกินไป

2

การขยายตัวของ*เรียงลำดับคุณสามารถทำให้มันเร็วขึ้นเล็กน้อยถ้าคุณไม่ต้องการเรียงลำดับ ในzsh:

for f (*(oN)) printf '%s\n' $f

หรือเพียงแค่:

printf '%s\n' *(oN)

bashไม่เทียบเท่าเท่าที่ฉันสามารถบอกได้ดังนั้นคุณต้องหันไปfindใช้

3

find . ! -name . -prune ! -name '.*' -print0 |
  while IFS= read -rd '' f; do
    printf '%s\n' "$f"
  done

(ด้านบนใช้-print0ส่วนขยายที่ไม่ได้มาตรฐานของGNU / BSD )

ที่ยังคงเกี่ยวข้องกับการวางคำสั่ง find และใช้การwhile readวนรอบช้าดังนั้นมันอาจจะช้ากว่าการใช้การforวนซ้ำเว้นแต่ว่ารายการของไฟล์จะมีขนาดใหญ่มาก

4

นอกจากนี้ตรงกันข้ามกับการขยายตัวของเชลล์ไวด์การ์ดfindจะทำการlstatเรียกใช้ระบบในแต่ละไฟล์ดังนั้นจึงไม่น่าที่การคัดแยกจะชดเชยสิ่งนั้น

ด้วย GNU / BSD findที่สามารถหลีกเลี่ยงได้โดยใช้-maxdepthส่วนขยายซึ่งจะทำให้การเพิ่มประสิทธิภาพการบันทึกlstat:

find . -maxdepth 1 ! -name '.*' -print0 |
  while IFS= read -rd '' f; do
    printf '%s\n' "$f"
  done

เนื่องจากfindเริ่มส่งชื่อไฟล์ทันทีที่พบ (ยกเว้นการบัฟเฟอร์เอาต์พุต stdio) ซึ่งอาจเร็วกว่าหากสิ่งที่คุณทำในลูปใช้เวลานานและรายการชื่อไฟล์มากกว่าบัฟเฟอร์ stdio (4 / 8 kB) ในกรณีดังกล่าวการประมวลผลภายในลูปจะเริ่มต้นก่อนที่จะfindค้นหาไฟล์ทั้งหมดเสร็จสิ้น ในระบบ GNU และ FreeBSD คุณอาจใช้stdbufเพื่อทำให้เกิดขึ้นเร็วขึ้น (ปิดการใช้งานบัฟเฟอร์ stdio)

5

วิธี POSIX / มาตรฐาน / แบบพกพาในการรันคำสั่งสำหรับแต่ละไฟล์ด้วยfindคือการใช้เพรดิเคต-exec:

find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'

ในกรณีechoที่มีประสิทธิภาพน้อยกว่าการวนลูปในเชลล์เนื่องจากเชลล์จะมีบิวด์อินechoในขณะที่findจะต้องวางกระบวนการใหม่และดำเนินการ/bin/echoในแต่ละไฟล์

หากคุณต้องการเรียกใช้หลายคำสั่งคุณสามารถทำได้:

find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'

แต่ระวังว่าcmd2จะดำเนินการเฉพาะถ้าcmd1ประสบความสำเร็จ

6

วิธีมาตรฐานเพื่อเรียกใช้คำสั่งที่ซับซ้อนสำหรับแต่ละไฟล์คือการเรียกเชลล์ด้วย-exec ... {} +:

find . ! -name . -prune ! -name '.*' -exec sh -c '
  for f do
    cmd1 "$f"
    cmd2 "$f"
  done' sh {} +

เวลานั้นเรากลับมามีประสิทธิภาพด้วยechoเนื่องจากเราใช้shbuiltin one และ-exec +รุ่นวางไข่ให้น้อยshที่สุดเท่าที่จะทำได้

7

ในการทดสอบของฉันในไดเรกทอรีที่มี 200,000 ไฟล์ที่มีชื่อสั้น ๆ ใน ext4 zshหนึ่ง (วรรค 2) คือเร็วที่สุดตามด้วยfor i in *ห่วงง่ายแรก(แม้ว่าตามปกติbashจะช้ากว่าหอยอื่น ๆ )


สิ่งที่ไม่!ต้องทำในการค้นหาคำสั่งหรือไม่
rubo77

@ rubo77 !สำหรับการปฏิเสธ ! -name . -prune more...จะทำ-prune(และmore...ตั้งแต่-pruneเสมอกลับจริง) สำหรับทุกไฟล์ .แต่ ดังนั้นมันจะทำmore...กับไฟล์ทั้งหมดที่อยู่ใน.แต่จะไม่รวมและจะไม่ลงในไดเรกทอรีย่อยของ. .ดังนั้นจึงเป็นที่เทียบเท่ามาตรฐานของ -mindepth 1 -maxdepth 1GNU
Stéphane Chazelas

18

ฉันลองสิ่งนี้ในไดเรกทอรีที่มี 2259 รายการและใช้timeคำสั่ง

ผลลัพธ์ของtime for f in *; do echo "$f"; done(ลบไฟล์!) คือ:

real    0m0.062s
user    0m0.036s
sys     0m0.012s

ผลลัพธ์ของtime find * -prune | while read f; do echo "$f"; done(ลบไฟล์!) คือ:

real    0m0.131s
user    0m0.056s
sys     0m0.060s

ฉันวิ่งแต่ละคำสั่งหลายครั้งเพื่อกำจัดแคชคิดถึง สิ่งนี้ชี้ให้เห็นว่าการเก็บไว้ในbash(สำหรับ i ใน ... ) นั้นเร็วกว่าการใช้findและการวางท่อเอาต์พุต (เป็นbash)

เพียงเพื่อความสมบูรณ์ฉันทิ้งท่อfindเนื่องจากในตัวอย่างของคุณมันซ้ำซ้อนทั้งหมด ผลลัพธ์ของการใช้เพียงfind * -prune:

real    0m0.053s
user    0m0.016s
sys     0m0.024s

นอกจากนี้time echo *(เอาท์พุทไม่ได้ขึ้นบรรทัดใหม่คั่นด้วยอนิจจา):

real    0m0.009s
user    0m0.008s
sys     0m0.000s

ณ จุดนี้ฉันสงสัยว่าเหตุผลที่echo *เร็วกว่าคือมันไม่ได้แสดงบรรทัดใหม่จำนวนมากดังนั้นเอาต์พุตจึงไม่เลื่อนมากนัก มาทดสอบกันสิ ...

time find * -prune | while read f; do echo "$f"; done > /dev/null

อัตราผลตอบแทน:

real    0m0.109s
user    0m0.076s
sys     0m0.032s

ในขณะที่time find * -prune > /dev/nullผลผลิต:

real    0m0.027s
user    0m0.008s
sys     0m0.012s

และtime for f in *; do echo "$f"; done > /dev/nullผลผลิต:

real    0m0.040s
user    0m0.036s
sys     0m0.004s

และในที่สุด: time echo * > /dev/nullอัตราผลตอบแทน:

real    0m0.011s
user    0m0.012s
sys     0m0.000s

ความแตกต่างบางอย่างสามารถพิจารณาได้จากปัจจัยสุ่ม แต่ดูเหมือนชัดเจน:

  • การส่งออกช้า
  • ค่าใช้จ่ายท่อเล็กน้อย
  • for f in *; do ...ช้ากว่าfind * -pruneด้วยตัวมันเอง แต่สำหรับการก่อสร้างด้านบนที่เกี่ยวข้องกับท่อนั้นเร็วกว่า

นอกจากนี้วิธีการทั้งสองดูเหมือนจะจัดการชื่อด้วยช่องว่างได้ดี

แก้ไข:

กำหนดเวลาสำหรับfind . -maxdepth 1 > /dev/nullvs. find * -prune > /dev/null:

time find . -maxdepth 1 > /dev/null:

real    0m0.018s
user    0m0.008s
sys     0m0.008s

find * -prune > /dev/null:

real    0m0.031s
user    0m0.020s
sys     0m0.008s

ดังนั้นข้อสรุปเพิ่มเติม:

  • find * -pruneจะช้ากว่าfind . -maxdepth 1- ในอดีตเปลือกกำลังประมวลผล glob แล้วสร้าง (ขนาดใหญ่) findบรรทัดคำสั่งสำหรับ หมายเหตุ: ผลตอบแทนเพียง find . -prune.

การทดสอบเพิ่มเติม time find . -maxdepth 1 -exec echo {} \; >/dev/null::

real    0m3.389s
user    0m0.040s
sys     0m0.412s

สรุป:

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

ท่อใดซ้ำซ้อนกัน คุณสามารถแสดงบรรทัดที่คุณใช้โดยไม่ใช้ท่อได้หรือไม่
rubo77

2
@ rubo77 find * -prune | while read f; do echo "$f"; doneมีท่อซ้ำซ้อน - ท่อทั้งหมดกำลังทำหน้าที่ส่งfindออกสิ่งที่ส่งออกด้วยตนเอง หากไม่มีท่อก็จะเป็นเพียงfind * -prune ท่อที่ซ้ำซ้อนโดยเฉพาะเพราะสิ่งที่อยู่อีกด้านหนึ่งของท่อเพียงคัดลอก stdin ไปยัง stdout (ส่วนใหญ่) มันไม่มีค่าใช้จ่ายแพง หากคุณต้องการทำสิ่งต่างๆด้วยผลลัพธ์ของการค้นหานอกจากจะคายมันออกมาอีกครั้งนั่นแตกต่างกัน
ฟิล

บางที timeconsuming *หลักคือ ในฐานะที่เป็นBitsOfNixระบุ: ผมยังขอแนะนำที่จะไม่ใช้*และ.สำหรับfindแทน
rubo77

@ rubo77 ดูเหมือนว่าจะเป็นอย่างนั้น ฉันเดาว่าฉันมองข้ามไป ฉันได้เพิ่มสิ่งที่ค้นพบสำหรับระบบของฉัน ผมถือว่าfind . -pruneเป็นเร็วขึ้นเพราะfindจะอ่านรายการไดเรกทอรีคำต่อคำในขณะที่เปลือกจะทำเช่นเดียวกันที่อาจเกิดขึ้นกับการจับคู่ glob (ที่อาจจะเพิ่มประสิทธิภาพสำหรับ*) findจากนั้นสร้างบรรทัดคำสั่งขนาดใหญ่สำหรับ
Phil

1
find . -pruneพิมพ์.บนระบบของฉันเท่านั้น มันเกือบจะไม่ทำงานเลย ไม่เหมือนกันfind * -pruneทั้งหมดที่แสดงชื่อทั้งหมดในไดเรกทอรีปัจจุบัน read fไฟล์จะเปลือยชื่อไฟล์ที่มีช่องว่างนำ
Ian D. Allen

10

ฉันจะไปค้นหาด้วยแน่นอนแม้ว่าฉันจะเปลี่ยนการค้นหาของคุณเป็นแบบนี้:

find . -maxdepth 1 -exec echo {} \;

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

ผมบอกว่าหาดีกว่าตั้งแต่ที่มีของคุณจะต้องมีการขยายตัวเป็นครั้งแรกและฉันกลัวว่าถ้าคุณมีไดเรกทอรีที่มีจำนวนมากของไฟล์มันอาจทำให้ข้อผิดพลาดรายการอาร์กิวเมนต์ยาวเกินไป กันไปสำหรับfor*find *

ตัวอย่างเช่นในระบบใดระบบหนึ่งที่ฉันใช้อยู่ในปัจจุบันมีไดเรกทอรีบางส่วนที่มีไฟล์มากกว่า 2 ล้านไฟล์ (<100k ต่อไฟล์):

find *
-bash: /usr/bin/find: Argument list too long

ฉันเพิ่ม-pruneเพื่อทำให้ทั้งสองตัวอย่างเหมือนกันมากขึ้น และฉันชอบไปป์ด้วยในขณะที่ดังนั้นจึงง่ายต่อการใช้คำสั่งเพิ่มเติมในการวนรอบ
rubo77


การเปลี่ยนขีด จำกัด นั้นยากที่จะแก้ไขจาก POV ของฉัน พิเศษเมื่อพูดถึงไฟล์กว่า 2 ล้านไฟล์ หากไม่มีการพูดนอกเรื่องจากคำถามสำหรับกรณีง่าย ๆ เช่นเดียวกับไดเรกทอรีระดับหนึ่งนั้นจะเร็วกว่า แต่ถ้าคุณเปลี่ยนโครงสร้างไฟล์ / ไดเรกทอรีจะเป็นการยากที่จะย้าย ในขณะที่ค้นหาและมีตัวเลือกมากมายที่คุณสามารถเตรียมพร้อมได้ดีกว่า แต่ถึงกระนั้นฉันยังขอแนะนำอย่างยิ่งที่จะไม่ใช้ * และ หาแทน มันจะเป็นแบบพกพามากกว่า * ที่คุณอาจไม่สามารถควบคุม hardlimit ได้ ...
BitsOfNix

4
ที่จะวางไข่หนึ่งสะท้อนกระบวนการต่อไฟล์ (ในขณะที่ในเปลือกสำหรับวงก็สะท้อนในตัวที่จะถูกนำมาใช้โดยไม่ต้องฟอร์กกระบวนการพิเศษ) และจะลงมาลงในไดเรกทอรีดังนั้นมันจะมากช้าลง โปรดทราบด้วยว่ามันจะรวมไฟล์ dot ไว้ด้วย
Stéphane Chazelas

คุณพูดถูกฉันเพิ่ม maxdepth 1 เพื่อให้มันติดอยู่กับระดับปัจจุบันเท่านั้น
BitsOfNix

7
find * -prune | while read f; do 
    echo "$f"
done

เป็นการใช้ประโยชน์อย่างไร้ประโยชน์find- สิ่งที่คุณพูดมีประสิทธิภาพ "สำหรับแต่ละไฟล์ในไดเรกทอรี ( *) ไม่พบไฟล์ใด ๆ นอกจากนี้มันไม่ปลอดภัยด้วยเหตุผลหลายประการ:

  • backslashes ในเส้นทางที่ได้รับการปฏิบัติเป็นพิเศษโดยไม่ต้องเลือกที่จะ-r readนี่ไม่ใช่ปัญหาของforลูป
  • การขึ้นบรรทัดใหม่ในเส้นทางจะทำให้การทำงานที่ไม่สำคัญภายในลูปเกิดขึ้น นี่ไม่ใช่ปัญหาของforลูป

การจัดการชื่อไฟล์ใด ๆ ด้วยfindเป็นเรื่องยากดังนั้นคุณควรใช้forตัวเลือกการวนซ้ำเมื่อทำได้ด้วยเหตุผลนั้นเพียงอย่างเดียว นอกจากนี้ยังมีการเรียกใช้โปรแกรมภายนอกเช่นความประสงค์โดยทั่วไปจะช้ากว่าที่เรียกใช้คำสั่งห่วงภายในเช่นfindfor


@ I0b0 สิ่งที่เกี่ยวกับ find -path './*' -prune หรือ find -path './ه^. เหมือนกัน' '-prune (เพื่อหลีกเลี่ยงไฟล์และไดเรกทอรีที่ซ่อนอยู่) เป็นโครงสร้างที่ดีกว่า - ในรูปแบบเต็ม: find -path' ./* '-prune -print0 | xargs -0 sh -c '... '?
AsymLabs

1
ทั้งfind's -print0มิได้xargs' -0เป็น POSIX ได้และคุณไม่สามารถใส่คำสั่งโดยพลในsh -c ' ... '(ราคาเดียวไม่สามารถหนีออกมาได้ภายในราคาเดียว) ดังนั้นจึงไม่ได้ค่อนข้างง่ายดังนั้น
l0b0

4

แต่เราเป็นคนดูดสำหรับคำถามเกี่ยวกับประสิทธิภาพ! คำขอสำหรับการทดสอบนี้มีสมมติฐานอย่างน้อยสองข้อที่ทำให้ไม่สามารถใช้งานได้อย่างมาก

A. สมมติว่าพวกเขาพบไฟล์เดียวกัน ...

ดีที่พวกเขาจะหาไฟล์เดียวกันในตอนแรกเพราะพวกเขาทั้งสอง iterating กว่า glob *เดียวกันคือ แต่find * -prune | while read fมีข้อบกพร่องหลายประการที่ทำให้เป็นไปได้ค่อนข้างจะไม่พบไฟล์ทั้งหมดที่คุณคาดหวัง:

  1. การค้นหา POSIX ไม่รับประกันว่าจะยอมรับอาร์กิวเมนต์มากกว่าหนึ่งพา ธ findการใช้งานส่วนใหญ่ทำ แต่ถึงกระนั้นคุณไม่ควรพึ่งพาสิ่งนั้น
  2. find *ARG_MAXสามารถทำลายเมื่อคุณกด for f in *จะไม่เพราะARG_MAXใช้กับexecไม่ได้สร้างขึ้น
  3. while read fสามารถตัดกับชื่อไฟล์ที่เริ่มต้นและลงท้ายด้วย whitespace ซึ่งจะถูกตัดออก คุณสามารถเอาชนะสิ่งนี้ได้ด้วยwhile readและพารามิเตอร์เริ่มต้นREPLYแต่ก็ยังไม่ช่วยคุณเมื่อมันมาถึงชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ในพวกเขา

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

printf '%s\n' *
find . -mindepth 1 -maxdepth 1 # for dotted names, too

whileไพพ์ไปยังลูปที่นี่สร้างเชลล์ย่อยโดยนัยที่ปิดเมื่อลูปสิ้นสุดซึ่งอาจไม่สะดวกสำหรับบางคน

เพื่อตอบคำถามนี่คือผลลัพธ์ในไดเรกทอรีของฉันที่มี 184 ไฟล์และไดเรกทอรีอยู่ในนั้น

$ time bash -c 'for i in {0..1000}; do find * -prune | while read f; do echo "$f"; done >/dev/null; done'

real    0m7.998s
user    0m5.204s
sys 0m2.996s
$ time bash -c 'for i in {0..1000}; do for f in *; do echo "$f"; done >/dev/null; done'

real    0m2.734s
user    0m2.553s
sys 0m0.181s
$ time bash -c 'for i in {0..1000}; do printf '%s\n' * > /dev/null; done'

real    0m1.468s
user    0m1.401s
sys 0m0.067s

$ time bash -c 'for i in {0..1000}; do find . -mindepth 1 -maxdepth 1 >/dev/null; done '

real    0m1.946s
user    0m0.847s
sys 0m0.933s

ฉันไม่เห็นด้วยกับคำสั่งในขณะที่วงวางไข่ subshell - ในกรณีที่เลวร้ายที่สุดหัวข้อใหม่: ต่อไปนี้จะพยายามที่จะแสดงก่อนและหลังขอโทษสำหรับการจัดรูปแบบที่ไม่ดี$ ps ax | grep bash 20784 pts/1 Ss 0:00 -bash 20811 pts/1 R+ 0:00 grep bash $ while true; do while true; do while true; do while true; do while true; do sleep 100; done; done; done; done; done ^Z [1]+ Stopped sleep 100 $ bg [1]+ sleep 100 & $ ps ax | grep bash 20784 pts/1 Ss 0:00 -bash 20924 pts/1 S+ 0:00 grep bash
Phil

ในทางเทคนิคฉัน misspoke: ท่อทำให้เกิด subshell โดยนัยไม่ใช่ห่วง while ฉันจะแก้ไข
kojiro

2

find *จะไม่ทำงานหาก*สร้างโทเค็นที่ดูเหมือนภาคแสดงมากกว่าเส้นทาง

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

เพื่อแก้ไขปัญหานี้คุณสามารถใช้find ./*แทน แต่แล้วมันก็ไม่ได้สร้างสตริงเหมือนfor x in *กันทั้งหมด

โปรดทราบว่าไม่จริงใช้การทำงานของสแกนfind ./* -prune | while read f .. findมันเป็นไวยากรณ์ globbing ./*ซึ่งจริง ๆ แล้วสำรวจไดเรกทอรีและสร้างชื่อ จากนั้นfindโปรแกรมจะต้องทำการstatตรวจสอบชื่อเหล่านั้นอย่างน้อยหนึ่งชื่อ คุณมีค่าใช้จ่ายในการเรียกใช้โปรแกรมและเข้าถึงไฟล์เหล่านี้จากนั้นทำ I / O เพื่ออ่านเอาต์พุต

มันเป็นเรื่องยากที่จะจินตนาการว่ามันอาจจะเป็นอะไร for x in ./* ...แต่มีประสิทธิภาพน้อยกว่า


1

สำหรับ starters forนั้นเป็นคีย์เวิร์ดของเชลล์ที่สร้างไว้ใน Bash ในขณะที่findสามารถแยกได้

$ type -a for
for is a shell keyword

$ type -a find
find is /usr/bin/find

การforวนซ้ำจะค้นหาเฉพาะไฟล์จากตัวละคร globstar เมื่อมันขยายออกมันจะไม่ถูกเรียกคืนไปยังไดเรกทอรีใด ๆ ที่พบ

การค้นหาในทางกลับกันจะได้รับรายการที่ขยายโดย globstar แต่มันจะค้นหาไฟล์และไดเรกทอรีทั้งหมดซ้ำ ๆ ด้านล่างรายการที่ขยายและท่อแต่ละอันผ่านไปยังwhileลูป

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

นั่นคือทั้งหมดที่ฉันคิดได้ว่าควรจะพูดถึงวิธีการทั้งสองนี้


ฉันเพิ่ม -prune ให้กับคำสั่ง find ดังนั้นจึงเหมือนกันมากกว่า
rubo77

0

หากไฟล์ทั้งหมดที่ส่งคืนโดย find สามารถประมวลผลได้ด้วยคำสั่งเดียว (เห็นได้ชัดว่าไม่ได้ใช้กับตัวอย่าง echo ของคุณด้านบน) คุณสามารถใช้ xargs:

find * |xargs some-command

0

เป็นเวลาหลายปีที่ฉันได้ใช้สิ่งนี้: -

find . -name 'filename'|xargs grep 'pattern'|more

เพื่อค้นหาไฟล์บางไฟล์ (เช่น * .txt) ที่มีรูปแบบที่ grep สามารถค้นหาและไพพ์ลงในเพิ่มเติมเพื่อไม่ให้เลื่อนออกจากหน้าจอ บางครั้งฉันใช้ไพพ์ >> เพื่อเขียนผลลัพธ์ไปยังไฟล์อื่นที่ฉันสามารถดูในภายหลัง

นี่คือตัวอย่างของผลลัพธ์: -

./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:Message-ID: <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:In-Reply-To: <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:  <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:  <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:Message-ID: <448E53556A3F442ABC58203D6281923E@hypermax>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2011-April.txt:URL: http://mylist.net/private/rodgersorganusers/attachments/20110420/3f
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.