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

Gre*_*hal 82 grep find

哪个更有效地查找整个文件系统中的哪些文件包含字符串:递归 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' {} \;

Sté*_*las 102

我不知道:

grep -r -i 'the brown dog' /*
Run Code Online (Sandbox Code Playgroud)

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

假设你的意思是:

grep -r -i 'the brown dog' /
Run Code Online (Sandbox Code Playgroud)

需要注意的几点:

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

您的:

find / -type f -exec grep -i 'the brown dog' {} \;
Run Code Online (Sandbox Code Playgroud)

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

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

find / -type f -exec grep -i 'the brown dog' {} +
Run Code Online (Sandbox Code Playgroud)

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

find / -type f -exec grep -i 'the brown dog' /dev/null {} +
Run Code Online (Sandbox Code Playgroud)

或使用 GNU grep

find / -type f -exec grep -Hi 'the brown dog' {} +
Run Code Online (Sandbox Code Playgroud)

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

使用 GNU 工具:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'
Run Code Online (Sandbox Code Playgroud)

如上所述,grep将运行尽可能少的实例,但find将继续查找更多文件,而第一次grep调用正在查找第一批内部。不过,这可能是也可能不是优势。例如,与存储在旋转硬盘中的数据,findgrep存储在磁盘上的不同位置访问数据会减慢磁盘吞吐量通过使磁头不断移动。在 RAID 设置(其中findgrep可能访问不同的磁盘)或 SSD 上,这可能会产生积极的影响。

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

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'
Run Code Online (Sandbox Code Playgroud)

可能会显着提高性能。但是请注意,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'
Run Code Online (Sandbox Code Playgroud)

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

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +
Run Code Online (Sandbox Code Playgroud)

或者修剪要明确排除的路径:

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

  • @Poldie,在 [Solaris 手册页](http://www.freebsd.org/cgi/man.cgi?query=find&apropos=0&sektion=0&manpath=SunOS% 205.10&arch=默认&格式=html) (3认同)

Hau*_*ing 16

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

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

但是让我们看看系统调用统计信息:

> 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
Run Code Online (Sandbox Code Playgroud)

仅 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
Run Code Online (Sandbox Code Playgroud)


Naf*_*Kay 7

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

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'
Run Code Online (Sandbox Code Playgroud)

这将根据find找到的内容同时执行多达 8 个 grep 进程。

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