Tại sao lệnh Lệnh tìm | grep 'tên tệp' chậm hơn rất nhiều so với tìm thấy tên tập tin 'tên?


10

Tôi đã thử cả hai lệnh và lệnh find | grep 'filename' chậm hơn nhiều lần so với find 'filename' lệnh đơn giản .

Điều gì sẽ là một lời giải thích thích hợp cho hành vi này?


2
Bạn đang liệt kê mọi tệp với find và sau đó chuyển dữ liệu tới grep để xử lý. Với tìm kiếm được sử dụng trên chính nó, bạn đang thiếu bước chuyển mọi tệp được liệt kê để grep để phân tích đầu ra. Điều này do đó sẽ nhanh hơn.
Raman sailopal

Chậm hơn theo nghĩa nào? Liệu các lệnh mất một lượng thời gian khác nhau để hoàn thành?
Kusalananda

1
Tôi không thể tái tạo điều này tại địa phương. Nếu bất cứ điều gì, time find "$HOME" -name '.profile'báo cáo một thời gian dài hơn time find "$HOME" | grep -F '.profile'. (17s so với 12s).
Kusalananda

2
@JenniferAnderson Tôi chạy cả hai lần. 17 và 12 giây là trung bình. Và có, grepbiến thể sẽ khớp bất cứ nơi nào trong findkết quả, trong khi kết hợp với find -namesẽ chỉ khớp chính xác (trong trường hợp này).
Kusalananda

2
Vâng, find filename sẽ nhanh thôi. Tôi nghĩ rằng đây là một lỗi đánh máy và OP có nghĩa là find -name filename. Với find filename, chỉ filenamesẽ được kiểm tra (và không có gì khác).
Kusalananda

Câu trả lời:


11

(Tôi đang giả sử GNU findở đây)

Chỉ sử dụng

find filename

sẽ nhanh chóng, bởi vì nó sẽ trả về filenamehoặc tên bên trong filenamenếu đó là thư mục hoặc lỗi nếu tên đó không tồn tại trong thư mục hiện tại. Đây là một hoạt động rất nhanh, tương tự ls filename(nhưng đệ quy nếu filenamelà một thư mục).

Ngược lại,

find | grep filename

sẽ cho phép findtạo một danh sách tất cả các tên từ thư mục hiện tại và bên dưới, grepsau đó sẽ lọc. Đây rõ ràng sẽ là một hoạt động chậm hơn nhiều.

Tôi cho rằng những gì thực sự có ý định là

find . -type f -name 'filename'

Đây sẽ filenamelà tên của một tệp thông thường ở bất cứ đâu trong thư mục hiện tại hoặc bên dưới.

Điều này sẽ nhanh chóng (hoặc tương đối nhanh) find | grep filename, nhưng grepgiải pháp sẽ phù hợp filenamevới đường dẫn đầy đủ của mỗi tên được tìm thấy, tương tự như những gì -path '*filename*'sẽ làm với find.


Sự nhầm lẫn đến từ một sự hiểu lầm về cách làm findviệc.

Tiện ích này có một số đường dẫn và trả về tất cả các tên bên dưới các đường dẫn này.

Sau đó, bạn có thể hạn chế các tên được trả về bằng các thử nghiệm khác nhau có thể tác động đến tên tệp, đường dẫn, dấu thời gian, kích thước tệp, loại tệp, v.v.

Khi bạn nói

find a b c

bạn hỏi findđể liệt kê tất cả các tên phát hành theo ba con đường a, bc. Nếu đây là tên của các tệp thông thường trong thư mục hiện tại, thì chúng sẽ được trả về. Nếu bất kỳ tên nào trong số chúng là tên của một thư mục, thì nó sẽ được trả về cùng với tất cả các tên khác trong thư mục đó.

Khi tôi làm

find . -type f -name 'filename'

Điều này tạo ra một danh sách tất cả các tên trong thư mục hiện tại ( .) và bên dưới. Sau đó, nó hạn chế những cái tên cho những file bình thường, tức là không phải thư mục vv với -type f. Sau đó, có một hạn chế hơn nữa đối với tên phù hợp filenamebằng cách sử dụng -name 'filename'. Chuỗi filenamecó thể là một mẫu tên tập tin tên, chẳng hạn như *.txt(chỉ cần nhớ trích dẫn nó!).

Thí dụ:

Sau đây dường như "tìm" tệp được gọi .profiletrong thư mục nhà của tôi:

$ pwd
/home/kk
$ find .profile
.profile

Nhưng trên thực tế, nó chỉ trả về tất cả các tên trong đường dẫn .profile(chỉ có một tên và đó là của tệp này).

Sau đó, tôi cdlên một cấp và thử lại:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

Các findlệnh bây giờ không thể tìm thấy bất kỳ con đường được gọi là .profile.

Tuy nhiên, nếu tôi làm cho nó nhìn vào thư mục hiện tại, và sau đó giới hạn các tên được trả về chỉ.profile , nó cũng tìm thấy nó từ đó:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenamesẽ chỉ trả về filenamenếu filenamekhông phải là thư mục loại (hoặc thuộc thư mục loại, nhưng không có bất kỳ mục nhập nào)
Stéphane Chazelas

2

Giải thích phi kỹ thuật: Tìm kiếm Jack trong đám đông nhanh hơn tìm kiếm mọi người trong đám đông và loại bỏ tất cả khỏi sự cân nhắc ngoại trừ Jack.


Vấn đề là OP đang kỳ vọng Jack sẽ là người duy nhất trong đám đông. Nếu có, họ may mắn. find jacksẽ liệt kê jacknếu đó là một tệp được gọi jackhoặc tất cả các tên trong thư mục nếu đó là một thư mục. Đó là một sự hiểu lầm về cách làm findviệc.
Kusalananda

1

Tôi chưa hiểu vấn đề nhưng có thể cung cấp thêm một số hiểu biết.

Giống như đối với Kusalananda, find | grepcuộc gọi rõ ràng nhanh hơn trên hệ thống của tôi, điều này không có nhiều ý nghĩa. Lúc đầu, tôi giả sử một số loại vấn đề đệm; việc ghi vào bảng điều khiển làm chậm thời gian đến tòa nhà kế tiếp để đọc tên tệp tiếp theo. Ghi vào một đường ống rất nhanh: khoảng 40MiB / giây ngay cả đối với ghi 32 byte (trên hệ thống khá chậm của tôi; 300 MiB / s cho kích thước khối 1MiB). Do đó, tôi giả định rằng findcó thể đọc từ hệ thống tệp nhanh hơn khi ghi vào ống (hoặc tệp) để hai thao tác đọc đường dẫn tệp và ghi vào bàn điều khiển có thể chạy song song (điều mà findmột quy trình xử lý đơn lẻ không thể tự thực hiện.

Đó findlà lỗi của

So sánh hai cuộc gọi

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

cho thấy findlàm điều gì đó cực kỳ ngu ngốc (bất cứ điều gì có thể). Nó chỉ là khá bất tài trong việc thực thi -name '*.txt'.

Có thể phụ thuộc vào tỷ lệ đầu vào / đầu ra

Bạn có thể nghĩ rằng find -namechiến thắng nếu có rất ít để viết. Nhưng tôi chỉ thấy xấu hổ hơn find. Nó bị mất ngay cả khi không có gì để ghi vào tất cả các tệp 200K (13M dữ liệu đường ống) cho grep:

time find /usr -name lwevhewoivhol

findcó thể nhanh như grepvậy

Nó chỉ ra rằng findsự ngu ngốc của namenó không mở rộng cho các thử nghiệm khác. Sử dụng regex thay thế và vấn đề không còn nữa:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Tôi đoán điều này có thể được coi là một lỗi. Bất cứ ai cũng sẵn sàng để báo cáo lỗi? Phiên bản của tôi là find (GNU findutils) 4.6.0


Làm thế nào lặp lại được thời gian của bạn? Nếu bạn đã thực hiện -namekiểm tra trước, thì nó có thể đã chậm hơn do nội dung thư mục không được lưu trữ. (Khi kiểm tra -name-regextôi thấy chúng mất khoảng thời gian gần như nhau, ít nhất một lần hiệu ứng bộ nhớ cache đã được xem xét. Tất nhiên nó có thể chỉ là một phiên bản khác của find...)
psmears

@psmears Tất nhiên, tôi đã thực hiện các bài kiểm tra này nhiều lần. Vấn đề bộ nhớ đệm đã được đề cập ngay cả trong các ý kiến ​​cho câu hỏi trước câu trả lời đầu tiên. findPhiên bản của tôi là find (GNU findutils) 4.6.0
Hauke ​​Laging

Tại sao nó đáng ngạc nhiên khi thêm -name '*.txt'chậm lại find? Nó phải làm thêm, kiểm tra từng tên tệp.
Barmar

@Barmar Một mặt công việc bổ sung này có thể được thực hiện cực kỳ nhanh chóng. Mặt khác, công việc làm thêm này tiết kiệm công việc khác. findphải ghi ít dữ liệu Và viết vào một đường ống là một hoạt động chậm hơn nhiều.
Hauke ​​Laging

Ghi vào đĩa rất chậm, ghi vào ống không quá tệ, nó chỉ sao chép vào bộ đệm kernel. Lưu ý rằng trong thử nghiệm đầu tiên của bạn, viết nhiều hơn /dev/nullbằng cách nào đó sử dụng ít thời gian hệ thống hơn .
Barmar

0

Lưu ý : Tôi sẽ cho rằng ý bạn là find . -name filename(nếu không, bạn đang tìm kiếm những thứ khác nhau; find filenamethực sự nhìn vào một đường dẫn gọi là tên tệp , có thể chứa hầu như không có tệp, do đó thoát ra rất nhanh).


Giả sử bạn có một thư mục chứa năm nghìn tệp. Trên hầu hết các hệ thống tệp, các tệp này thực sự được lưu trữ trong cấu trúc cây , cho phép nhanh chóng xác định vị trí của bất kỳ tệp nào.

Vì vậy, khi bạn hỏi findđể xác định vị trí một file có tên chỉ yêu cầu kiểm tra, findsẽ hỏi cho rằng tập tin, và duy nhất mà tập tin, để hệ thống tập tin cơ bản, mà sẽ đọc rất ít các trang từ các thiết bị lưu trữ. Vì vậy, nếu hệ thống tập tin đáng giá muối của nó, hoạt động này sẽ chạy nhanh hơn nhiều so với việc đi qua toàn bộ cây để lấy tất cả các mục.

findTuy nhiên, khi bạn hỏi đơn giản đó chính xác là những gì bạn làm, bạn đi ngang qua toàn bộ cây, đọc. Mỗi. Độc thân. Nhập cảnh. Với các thư mục lớn, đây có thể là một vấn đề (chính xác là lý do tại sao một số phần mềm, cần lưu trữ nhiều tệp trên đĩa, sẽ tạo ra "cây thư mục" sâu hai hoặc ba thành phần: theo cách này, mỗi một lá chỉ cần giữ ít hơn các tập tin).


-2

Giả sử tập tin / john / paul / george / ringo / beatles tồn tại và tập tin bạn đang tìm kiếm được gọi là 'đá'

find / stones

tìm sẽ so sánh 'beatles' với 'đá' và thả nó khi 's' và 'b' không khớp.

find / | grep stones

Trong trường hợp này, find sẽ chuyển '/ john / paul / george / ringo / beatles' cho grep và grep sẽ phải tìm đường đi qua toàn bộ đường dẫn trước khi xác định xem nó có khớp không.

Do đó, grep đang làm nhiều công việc hơn, đó là lý do tại sao phải mất nhiều thời gian hơn


1
Bạn đã thử chưa?
Hauke ​​Laging

3
Chi phí của các phép so sánh chuỗi (cực kỳ đơn giản và rẻ tiền) hoàn toàn bị chi phí bởi IO (hoặc chỉ là tòa nhà nếu được lưu trong bộ nhớ cache) của việc tra cứu thư mục.
Mat

grep không phải là so sánh chuỗi, so sánh biểu thức chính quy của nó có nghĩa là nó phải thực hiện theo cách của nó thông qua toàn bộ chuỗi cho đến khi tìm thấy kết quả khớp hoặc đến cuối. Các tra cứu thư mục là như nhau không có vấn đề gì.
Bệnh hoang tưởng

@Paranoid Hừm, bạn đang nói về phiên bản tìm kiếm nào? Nó dường như không bất cứ điều gì giống như tìm thấy tôi đang sử dụng để trong debian.
ống
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.