对于查找整个文件系统中哪些文件包含字符串,哪个更有效:递归 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
输出重定向到终端设备以外的任何设备,那么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'
为了避免查看内部/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¢。