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จะช้ากว่าหอยอื่น ๆ )
findไม่เปิดไฟล์ที่พบ สิ่งเดียวที่ฉันสามารถดูกัดคุณมาที่นี่ด้วยความเคารพเป็นจำนวนมากของไฟล์ที่เป็นARG_MAX