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'
แทนซึ่งเป็นprintf
builtin ก็จะหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้น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
เนื่องจากเราใช้sh
builtin one และ-exec +
รุ่นวางไข่ให้น้อยsh
ที่สุดเท่าที่จะทำได้
7
ในการทดสอบของฉันในไดเรกทอรีที่มี 200,000 ไฟล์ที่มีชื่อสั้น ๆ ใน ext4 zsh
หนึ่ง (วรรค 2) คือเร็วที่สุดตามด้วยfor i in *
ห่วงง่ายแรก(แม้ว่าตามปกติbash
จะช้ากว่าหอยอื่น ๆ )
find
ไม่เปิดไฟล์ที่พบ สิ่งเดียวที่ฉันสามารถดูกัดคุณมาที่นี่ด้วยความเคารพเป็นจำนวนมากของไฟล์ที่เป็นARG_MAX