1.
Cái đầu tiên:
for f in *; do
echo "$f"
done
không cho các tập tin được gọi -n, -evà các biến thể như -nenevà với một số triển khai bash, với tên tập tin chứa những dấu xồ nguợc.
Thư hai:
find * -prune | while read f; do
echo "$f"
done
không cho thậm chí nhiều trường hợp (file gọi !, -H, -name, (, tên tệp bắt đầu hoặc kết thúc bằng các đoạn trống hoặc chứa các ký tự xuống dòng ...)
Đó là trình bao mở rộng *, findkhông làm gì ngoài việc in các tệp mà nó nhận được dưới dạng đối số. Bạn cũng có thể đã sử dụng printf '%s\n'thay vì printfđược xây dựng cũng sẽ tránh được quá nhiều lỗi tiềm ẩn.
2.
Việc mở rộng *được sắp xếp, bạn có thể làm cho nó nhanh hơn một chút nếu bạn không cần sắp xếp. Trong zsh:
for f (*(oN)) printf '%s\n' $f
hoặc đơn giản:
printf '%s\n' *(oN)
bashkhông có gì tương đương như tôi có thể nói, vì vậy bạn cần phải dùng đến find.
3.
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(ở trên sử dụng -print0phần mở rộng không chuẩn GNU / BSD ).
Điều đó vẫn liên quan đến việc sinh ra một lệnh find và sử dụng một while readvòng lặp chậm , vì vậy nó có thể sẽ chậm hơn so với việc sử dụng forvòng lặp trừ khi danh sách các tệp rất lớn.
4.
Ngoài ra, trái với việc mở rộng ký tự đại diện, findsẽ thực hiện một lstatcuộc gọi hệ thống trên mỗi tệp, do đó, việc không sắp xếp sẽ bù đắp cho điều đó.
Với GNU / BSD find, điều đó có thể tránh được bằng cách sử dụng -maxdepthtiện ích mở rộng của chúng , điều này sẽ kích hoạt tối ưu hóa tiết kiệm lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Bởi vì findbắt đầu xuất tên tệp ngay khi tìm thấy chúng (ngoại trừ bộ đệm đầu ra stdio), trong đó có thể nhanh hơn nếu những gì bạn làm trong vòng lặp tốn thời gian và danh sách tên tệp nhiều hơn bộ đệm stdio (4 / 8 kB). Trong trường hợp đó, việc xử lý trong vòng lặp sẽ bắt đầu trước khi findkết thúc việc tìm tất cả các tệp. Trên các hệ thống GNU và FreeBSD, bạn có thể sử dụng stdbufđể khiến điều đó xảy ra sớm hơn (vô hiệu hóa bộ đệm stdio).
5.
Cách POSIX / tiêu chuẩn / di động để chạy các lệnh cho mỗi tệp findlà sử dụng biến -execvị ngữ:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Trong trường hợp echomặc dù, điều đó kém hiệu quả hơn so với thực hiện lặp trong shell vì shell sẽ có phiên bản dựng sẵn echotrong khi findsẽ cần phải tạo ra một quy trình mới và thực thi /bin/echotrong đó cho mỗi tệp.
Nếu bạn cần chạy một số lệnh, bạn có thể làm:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Nhưng hãy cẩn thận cmd2chỉ được thực hiện nếu cmd1thành công.
6.
Một cách chuẩn để chạy các lệnh phức tạp cho mỗi tệp là gọi shell với -exec ... {} +:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Lúc đó, chúng tôi trở lại hiệu quả echovì chúng tôi đang sử dụng shmột -exec +bản dựng sẵn và phiên bản sinh ra càng ít shcàng tốt.
7.
Trong các thử nghiệm của tôi trên một thư mục có 200.000 tệp có tên ngắn trên ext4, zshmột (đoạn 2.) là nhanh nhất, tiếp theo là for i in *vòng lặp đơn giản đầu tiên (mặc dù như thường lệ, bashchậm hơn rất nhiều so với các shell khác cho điều đó).
findkhông mở các tập tin mà nó tìm thấy. Điều duy nhất tôi có thể thấy khi cắn bạn ở đây đối với một số lượng lớn tệp là ARG_MAX .