递归 grep 与 find / -type f -exec grep {} \;哪个更有效/更快?

递归 grep 与 find / -type f -exec grep {} \;哪个更有效/更快?

对于查找整个文件系统中哪些文件包含字符串,哪个更有效:递归 grep 或在 exec 语句中使用 grep 进行查找?我认为 find 会更有效,因为如果您知道文件扩展名或与文件名匹配的正则表达式,您至少可以进行一些过滤,但是当您只知道-type f哪个更好时? GNU grep 2.6.3;查找(GNU findutils)4.4.2

例子:

grep -r -i 'the brown dog' /

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

答案1

我不知道:

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

确实是你的意思。这意味着在所有非隐藏文件和目录中递归地执行 grep /(但仍然查看其中的隐藏文件和目录)。

假设你的意思是:

grep -r -i 'the brown dog' /

有几点需要注意:

  • 并非所有grep实现都支持-r.在那些这样做的人中,行为有所不同:有些在遍历目录树时遵循目录的符号链接(这意味着您可能最终会在同一个文件中查找多次,甚至在无限循环中运行),有些则不会。有些会查看设备文件(例如,这将花费相当长的时间/dev/zero)或管道或二进制文件......,有些不会。
  • 它很有效,因为grep一旦发现文件就开始查看文件内部。但是,当它查找文件时,它不再寻找更多文件来搜索(在大多数情况下这可能也是如此)

你的:

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

(删除了-r此处没有意义的内容)效率非常低,因为您grep每个文件都运行一个。;只能用于只接受一个参数的命令。而且这里,因为grep只查找一个文件,所以不会打印文件名,所以你不知道匹配项在哪里。

您不是在查看设备文件、管道、符号链接...,您不是在跟踪符号链接,但您仍然可能在查看诸如/proc/mem.

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

会好很多,因为grep将运行尽可能少的命令。除非最后一次运行只有一个文件,否则您将获得文件名。为此,最好使用:

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

或使用 GNU grep

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

请注意,直到找到足够的文件供其处理grep后才会启动,因此会出现一些初始延迟。find并且在前一个文件返回find之前不会继续搜索更多文件。grep分配和传递大文件列表会产生一些(可能可以忽略不计)影响,因此总而言之,它可能比grep -r不遵循符号链接或不查看设备内部的文件效率低。

使用 GNU 工具:

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

如上所述,grep将运行尽可能少的实例,但在第一次调用在第一批内部查找find时将继续查找更多文件。grep但这可能是也可能不是一个优势。例如,当数据存储在旋转硬盘驱动器上时,find访问grep存储在磁盘上不同位置的数据会导致磁盘磁头不断移动,从而降低磁盘吞吐量。在 RAID 设置(find可能grep访问不同的磁盘)或 SSD 上,这可能会产生积极的影响。

在 RAID 设置中,运行多个同时 grep调用也可能会改善情况。仍然在 3 个磁盘的 RAID1 存储上使用 GNU 工具,

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

可能会显着提高性能。但请注意,grep只有找到足够的文件来填充第一个grep命令后,才会启动第二个命令。您可以添加一个-n选项,xargs以便更快地发生(并每次grep调用传递更少的文件)。

还要注意,如果您将xargs输出重定向到终端设备以外的任何设备,那么grepss 将开始缓冲其输出,这意味着这些greps 的输出可能会被错误地交错。您必须使用stdbuf -oL(在 GNU 或 FreeBSD 上可用的地方)来解决此问题(您可能仍会遇到非常长的行(通常 >4KiB)的问题)或让每个输出都写入单独的文件中,最后将它们全部连接起来。

在这里,您要查找的字符串是固定的(不是正则表达式),因此使用该-F选项可能会产生影响(不太可能,因为grep实现已经知道如何优化它)。

另一件可能产生重大影响的事情是将语言环境固定为 C(如果您处于多字节语言环境中):

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

为了避免查看内部/proc/sys...,请使用-xdev并指定要在其中搜索的文件系统:

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

或者修剪您想要显式排除的路径:

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

答案2

如果*调用中的grep对您来说并不重要,那么第一个应该更有效,因为只grep启动了一个实例,并且分叉不是免费的。在大多数情况下,即使使用 ,它也会更快,*但在边缘情况下,排序可能会逆转这种情况。

可能还有其他find结构grep可以更好地工作,尤其是对于许多小文件。一次读取大量文件条目和索引节点可能会提高旋转媒体的性能。

但让我们看一下系统调用统计数据:

寻找

> 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

仅 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

答案3

如果您使用的是 SSD 并且寻道时间可以忽略不计,您可以使用 GNU 并行:

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

这将根据发现的内容同时执行最多 8 个 grep 进程find

这会严重影响硬盘驱动器,但 SSD 应该可以很好地应对。

答案4

关于这一点还需要考虑的一件事如下。

任何目录grep将必须递归地遍历包含比系统更多的文件无文件环境? (例如打开文件句柄的数量,大多数 Linux 发行版上的默认值为 1024)

如果是这样,那么寻找绝对是可行的方法,因为某些版本grep将用一个轰炸参数列表太长当它到达的目录中的文件数量超过最大打开文件处理设置时,会出现错误。

只是我的2¢。

相关内容