Grep đệ quy vs find / -type f -exec grep {} \; Cái nào hiệu quả hơn / nhanh hơn?


70

Cái nào hiệu quả hơn cho việc tìm tập tin nào trong toàn bộ hệ thống tập tin chứa một chuỗi: grep đệ quy hoặc tìm với grep trong câu lệnh exec? Tôi giả sử find sẽ hiệu quả hơn vì ít nhất bạn có thể thực hiện một số bộ lọc nếu bạn biết phần mở rộng tệp hoặc biểu thức chính phù hợp với tên tệp, nhưng khi bạn chỉ biết -type fcái nào tốt hơn? GNU grep 2.6.3; tìm (tìm kiếm GNU) 4.4.2

Thí dụ:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;


1
Toán / khoa học máy tính / hiệu quả thuật toán không dựa trên ý kiến.
Gregg Leventhal

Kiểm tra cái này Mặc dù không đệ quy, nó sẽ cung cấp cho sự hiểu biết về cái nào tốt hơn. unix.stackexchange.com/questions/47983/ Mạnh
Ramesh

8
@AvinashRaj anh không hỏi ý kiến. Anh ấy đang hỏi cái nào hiệu quả hơn và / hoặc nhanh hơn , không phải cái nào "tốt hơn". Đây là một câu hỏi hoàn toàn có thể trả lời được, có một câu trả lời cụ thể, duy nhất phụ thuộc vào cách hai chương trình này thực hiện công việc của họ và chính xác những gì bạn cung cấp cho họ để tìm kiếm.
terdon

2
Lưu ý rằng -exec {} +biểu mẫu sẽ thực hiện ít dĩa hơn, vì vậy nên nhanh hơn -exec {} \;. Bạn có thể cần thêm -H(hoặc -h) vào các greptùy chọn để có đầu ra chính xác tương đương.
Mikel

Bạn có thể không muốn -rtùy chọn grepcho cái thứ hai
qwertzguy

Câu trả lời:


85

Tôi không chắc:

grep -r -i 'the brown dog' /*

thực sự là những gì bạn có ý nghĩa. Điều đó có nghĩa là grep đệ quy trong tất cả các tệp và thư mục không bị ẩn trong /(nhưng vẫn nhìn vào bên trong các tệp và thư mục ẩn bên trong các tệp đó).

Giả sử bạn có nghĩa là:

grep -r -i 'the brown dog' /

Một số điều cần lưu ý:

  • Không phải tất cả các grephỗ trợ thực hiện -r. Và trong số những hành động đó, các hành vi khác nhau: một số theo các liên kết tượng trưng đến các thư mục khi duyệt qua cây thư mục (có nghĩa là bạn có thể sẽ tìm kiếm nhiều lần trong cùng một tệp hoặc thậm chí chạy trong các vòng lặp vô hạn), một số thì không. Một số sẽ nhìn vào bên trong các tệp thiết bị ( /dev/zeroví dụ như sẽ mất khá nhiều thời gian ) hoặc các đường ống hoặc tệp nhị phân ..., một số thì không.
  • Nó hiệu quả khi grepbắt đầu tìm kiếm bên trong các tập tin ngay khi phát hiện ra chúng. Nhưng trong khi nó tìm trong một tệp, nó không còn tìm kiếm thêm tệp để tìm kiếm nữa (có lẽ cũng giống như vậy trong hầu hết các trường hợp)

Của bạn:

find / -type f -exec grep -i 'the brown dog' {} \;

(loại bỏ -rcái không có ý nghĩa ở đây) là không hiệu quả khủng khiếp vì bạn đang chạy một greptệp cho mỗi tệp. ;chỉ nên được sử dụng cho các lệnh chỉ chấp nhận một đối số. Hơn nữa, ở đây, vì grepchỉ nhìn trong một tệp, nó sẽ không in tên tệp, vì vậy bạn sẽ không biết các kết quả khớp ở đâu.

Bạn không tìm kiếm bên trong các tập tin thiết bị, đường ống, liên kết tượng trưng ..., bạn không theo dõi các liên kết tượng trưng, ​​nhưng bạn vẫn có khả năng nhìn vào bên trong những thứ như /proc/mem.

find / -type f -exec grep -i 'the brown dog' {} +

sẽ tốt hơn rất nhiều vì càng ít greplệnh càng tốt sẽ được chạy. Bạn sẽ nhận được tên tệp trừ khi lần chạy cuối cùng chỉ có một tệp. Vì vậy, tốt hơn là sử dụng:

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

hoặc với GNU grep:

find / -type f -exec grep -Hi 'the brown dog' {} +

Lưu ý rằng grepsẽ không được bắt đầu cho đến khi findtìm thấy đủ các tệp để nó nhai, do đó sẽ có một số độ trễ ban đầu. Và findsẽ không tiếp tục tìm kiếm thêm tập tin cho đến khi trước đó grepđã trở lại. Phân bổ và chuyển danh sách tệp lớn có một số tác động (có thể không đáng kể), do đó, tất cả có lẽ sẽ kém hiệu quả hơn so với grep -rviệc không theo liên kết tượng trưng hoặc nhìn vào bên trong thiết bị.

Với các công cụ GNU:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

Như trên, càng ít greptrường hợp càng tốt sẽ được chạy, nhưng findsẽ tiếp tục tìm kiếm nhiều tệp hơn trong khi lệnh đầu tiên grepđược tìm trong lô đầu tiên. Điều đó có thể hoặc không thể là một lợi thế mặc dù. Chẳng hạn, với dữ liệu được lưu trữ trên các ổ cứng quay findvà việc greptruy cập dữ liệu được lưu trữ tại các vị trí khác nhau trên đĩa sẽ làm chậm thông lượng đĩa bằng cách làm cho đầu đĩa di chuyển liên tục. Trong thiết lập RAID (nơi findgrepcó thể truy cập các đĩa khác nhau) hoặc trên SSD, điều đó có thể tạo ra sự khác biệt tích cực.

Trong thiết lập RAID, chạy một số lệnh đồng thời grep cũng có thể cải thiện mọi thứ. Vẫn với các công cụ GNU trên bộ lưu trữ RAID1 với 3 đĩa,

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

có thể tăng hiệu suất đáng kể. Tuy nhiên, lưu ý rằng cái thứ hai grepsẽ chỉ được bắt đầu khi đã tìm thấy đủ các tệp để điền vào greplệnh đầu tiên . Bạn có thể thêm một -ntùy chọn để xargsđiều đó xảy ra sớm hơn (và truyền ít tệp hơn cho mỗi lần grepgọi).

Cũng lưu ý rằng nếu bạn đang chuyển hướng xargsđầu ra sang bất cứ thứ gì ngoại trừ thiết bị đầu cuối, thì grepss sẽ bắt đầu đệm đầu ra của chúng, điều đó có nghĩa là đầu ra của những cái đó grepcó thể sẽ bị xen kẽ không chính xác. Bạn sẽ phải sử dụng stdbuf -oL(nếu có sẵn như trên GNU hoặc FreeBSD) để giải quyết vấn đề đó (bạn vẫn có thể gặp vấn đề với các dòng rất dài (thường> 4KiB)) hoặc mỗi lần ghi đầu ra của chúng vào một tệp riêng biệt và ghép chúng lại tất cả cuối cùng

Ở đây, chuỗi bạn đang tìm kiếm đã được sửa chữa (không phải là biểu thức chính quy) vì vậy sử dụng -Ftùy chọn này có thể tạo ra sự khác biệt (không chắc vì các greptriển khai đã biết cách tối ưu hóa điều đó rồi).

Một điều khác có thể tạo ra sự khác biệt lớn là sửa lỗi miền địa phương thành C nếu bạn ở miền địa phương nhiều byte:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

Để tránh nhìn vào bên trong /proc, /sys..., hãy sử dụng -xdevvà chỉ định hệ thống tệp bạn muốn tìm kiếm trong:

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

Hoặc cắt tỉa các đường dẫn bạn muốn loại trừ một cách rõ ràng:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

Tôi không cho rằng ai đó có thể chỉ cho tôi một tài nguyên - hoặc giải thích - ý nghĩa của {} và +. Không có gì tôi có thể thấy trong các trang hướng dẫn cho exec, grep hoặc tìm thấy trên hộp Solaris mà tôi đang sử dụng. Có phải chỉ là vỏ tên nối liền tên và chuyển chúng cho grep?

3
@Poldie, điều đó được giải thích rõ ràng trong phần mô tả -execvị ngữ trong trang người Solaris
Stéphane Chazelas

À, vâng. Tôi đã không thoát khỏi {char trong khi tìm kiếm trong trang người đàn ông. Liên kết của bạn là tốt hơn; Tôi thấy trang người đàn ông khủng khiếp để đọc.

1
RAID1 w / 3 đĩa? Thật kỳ lạ ...
tink

1
@tink, có RAID1 trên 2 đĩa trở lên. Với 3 đĩa so với 2 đĩa, bạn tăng hiệu suất dự phòng và hiệu suất đọc trong khi hiệu suất ghi gần như nhau. Với 3 đĩa trái ngược với 2, điều đó có nghĩa là bạn cũng có thể sửa lỗi, vì khi một chút lật trên một trong các bản sao, bạn có thể biết cái nào đúng bằng cách kiểm tra cả 3 bản trong khi với 2 đĩa, bạn không thể thực sự nói
Stéphane Chazelas

13

Nếu *trong grepcuộc gọi không phải là quan trọng với bạn thì người đầu tiên nên hiệu quả hơn khi chỉ có một thể hiện của grepđược bắt đầu, và nĩa không được tự do. Trong hầu hết các trường hợp, nó sẽ nhanh hơn ngay cả với *nhưng trong các trường hợp cạnh, việc sắp xếp có thể đảo ngược điều đó.

Có thể có khác find- grepcấu trúc mà làm việc tốt hơn đặc biệt với nhiều tập tin nhỏ. Đọc số lượng lớn các mục nhập và inodes cùng một lúc có thể cải thiện hiệu suất trên phương tiện quay.

Nhưng chúng ta hãy nhìn vào số liệu thống kê của tòa nhà:

tìm thấy

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

chỉ grep

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total

1
Trên quy mô tìm kiếm toàn bộ hệ thống tập tin, các nhánh là không đáng kể. I / O là những gì bạn muốn giảm.
Gilles

Mặc dù đó là lỗi từ OP, nhưng so sánh không chính xác, bạn nên xóa -rcờ grepkhi sử dụng find. Bạn có thể thấy rằng nó đã tìm kiếm nhiều lần cùng một tệp bằng cách so sánh số lượng openđiều đó đã xảy ra.
qwertzguy

1
@qwertzguy, không, -rnên vô hại vì -type fđảm bảo không có đối số nào là thư mục. Nhiều open()s có nhiều khả năng xuống các tệp khác được mở greptại mỗi lần gọi (thư viện, dữ liệu bản địa hóa ...) (cảm ơn vì đã chỉnh sửa câu trả lời của tôi btw)
Stéphane Chazelas

5

Nếu bạn đang sử dụng SSD và tìm kiếm thời gian là không đáng kể, bạn có thể sử dụng GNU song song:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

Điều này sẽ thực thi tối đa 8 quy trình grep cùng một lúc dựa trên những gì findtìm thấy.

Điều này sẽ đánh bại một ổ đĩa cứng, nhưng một ổ SSD nên đối phó khá tốt với nó.


-1

Một điều nữa để xem xét về điều này là như sau.

Bất kỳ thư mục nào mà grep sẽ phải đệ quy đi qua có chứa nhiều tệp hơn cài đặt nofile của hệ thống của bạn không? (ví dụ: số lượng xử lý tệp mở, mặc định là 1024 trên hầu hết các bản phân phối linux)

Nếu vậy, thì thấy chắc chắn là con đường để đi từ một số phiên bản của grep sẽ đánh bom ra với một danh sách Đối số quá lâu lỗi khi nó chạm một thư mục với nhiều file hơn so với tập tin mở tối đa xử lý thiết lập.

Chỉ cần 2 của tôi.


1
Tại sao sẽ grepném bom? Ít nhất là với GNU grep nếu bạn đưa ra một đường dẫn có dấu /và sử dụng -Rnó sẽ chỉ lặp đi lặp lại qua các thư mục. Các vỏ sẽ không mở rộng bất cứ điều gì trừ khi bạn cung cấp cho vỏ những đống. Vì vậy, trong ví dụ đã cho ( /*) chỉ có nội dung của /vật chất, không phải của các thư mục con sẽ được liệt kê đơn giản grep, không được chuyển qua làm đối số từ trình bao.
0xC0000022L

Vâng, xem xét các OP được hỏi về tìm kiếm đệ quy (ví dụ: "grep -r -i 'con chó nâu' / *"), tôi đã thấy GNU của grep (ít nhất là Version 2.9) đánh bom ra với: "- bash: / bin / grep: Danh sách đối số quá dài "bằng cách sử dụng tìm kiếm chính xác mà OP đã sử dụng trên một thư mục có hơn 140.000 thư mục con trong đó.
B.Kaatz
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.