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
, -e
và các biến thể như -nene
và 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 *
, find
khô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)
bash
khô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 -print0
phầ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 read
vòng lặp chậm , vì vậy nó có thể sẽ chậm hơn so với việc sử dụng for
vò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, find
sẽ thực hiện một lstat
cuộ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 -maxdepth
tiệ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ì find
bắ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 find
kế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 find
là sử dụng biến -exec
vị ngữ:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Trong trường hợp echo
mặ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 echo
trong khi find
sẽ cần phải tạo ra một quy trình mới và thực thi /bin/echo
trong đó 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 cmd2
chỉ được thực hiện nếu cmd1
thà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ả echo
vì chúng tôi đang sử dụng sh
một -exec +
bản dựng sẵn và phiên bản sinh ra càng ít sh
cà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, zsh
mộ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ệ, bash
chậm hơn rất nhiều so với các shell khác cho điều đó).
find
khô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 .