哪个更有效地查找整个文件系统中的哪些文件包含字符串:递归 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
我不知道:
Run Code Online (Sandbox Code Playgroud)grep -r -i 'the brown dog' /*
真的是你的意思。这意味着在所有非隐藏文件和目录中递归地 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
调用正在查找第一批内部。不过,这可能是也可能不是优势。例如,与存储在旋转硬盘中的数据,find
并grep
存储在磁盘上的不同位置访问数据会减慢磁盘吞吐量通过使磁头不断移动。在 RAID 设置(其中find
和grep
可能访问不同的磁盘)或 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
输出重定向到终端设备以外的任何设备,那么greps
s 将开始缓冲其输出,这意味着这些grep
s的输出可能会被错误地交错。您必须在它们上使用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)
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)
如果您使用的是 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 应该可以很好地应对它。