为什么命令“find | grep 'filename'”比“find 'filename'”慢得多?

为什么命令“find | grep 'filename'”比“find 'filename'”慢得多?

我尝试了这两个命令,该命令 find | grep 'filename' 比简单命令慢很多很多倍find 'filename'

对于这种行为的正确解释是什么?

答案1

(我假设find这里是 GNU)

仅使用

find filename

速度要快,因为它只会返回,或者如果它是目录则返回filename内部的名称,或者如果当前目录中不存在该名称则返回错误。filename这是一个非常快速的操作,类似于ls filename(但如果filename是目录则递归)。

相比之下,

find | grep filename

将允许find生成一个列表全部当前目录及以下目录中的名称,grep然后将进行过滤。这显然是一个慢得多的操作。

我假设是什么实际上本来的目的是

find . -type f -name 'filename'

这将在当前目录或以下目录中的任何位置查找filename常规文件的名称。

这将与 一样快(或相当快)find | grep filename,但grep解决方案将filename与每个找到的名称的完整路径进行匹配,类似于-path '*filename*'的处理方式find


这种混乱源于对find工作原理的误解。

该实用程序需要一些路径并返回这些路径下的所有名称。

然后您就可以限制使用可能作用于文件名、路径、时间戳、文件大小、文件类型等的各种测试返回的名称。

当你说

find a b c

您要求find列出三个路径ab和下的每个可用名称c。如果这些恰好是当前目录中常规文件的名称,则将返回这些名称。如果它们中的任何一个恰好是目录的名称,那么它将与该目录内的所有其他名称一起返回。

当我做

find . -type f -name 'filename'

.这会生成当前目录 ( ) 及以下目录中所有名称的列表。然后它将名称限制为常规文件的名称,即不是目录等,扩展名为-type f.然后对filename使用匹配的名称有进一步的限制-name 'filename'。该字符串filename可能是文件名通配模式,例如*.txt(只需记住引用它!)。

例子:

以下似乎“找到”.profile在我的主目录中调用的文件:

$ pwd
/home/kk
$ find .profile
.profile

但事实上,它只是返回路径中的所有名称.profile(只有一个名称,就是这个文件的名称)。

然后我cd提升一级并重试:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

find命令现在找不到任何名为 的路径.profile

但是,如果我让它查看当前目录,然后将返回的名称限制为仅.profile,它也从那里找到它:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

答案2

非技术解释:在人群中寻找杰克比寻找人群中的每个人并排除除杰克之外的所有人更快。

答案3

我还没有理解这个问题,但可以提供更多见解。

就像 Kusalananda 一样,find | grep在我的系统上调用显然更快,但这没有多大意义。起初我假设存在某种缓冲问题;写入控制台会减慢下一个系统调用读取下一个文件名的时间。写入管道的速度非常快:即使对于 32 字节写入,也约为 40MiB/s(在我相当慢的系统上;对于 1MiB 的块大小,为 300 MiB/s)。因此,我假设find在写入管道(或文件)时可以更快地从文件系统读取数据,以便读取文件路径和写入控制台的两个操作可以并行运行(find作为单线程进程无法自行完成)。

都是find它的错

比较两个调用

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

表明find做了一些极其愚蠢的事情(无论是什么)。事实证明它在执行方面非常无能-name '*.txt'

可能取决于输入/输出比

您可能会认为,find -name如果可写的内容很少,那就更好了。但它只会变得更加尴尬find。即使对于 200K 文件(13M 管道数据)根本没有任何可写入的内容,它也会丢失grep

time find /usr -name lwevhewoivhol

findgrep不过可以和 一样快

事实证明,find这种愚蠢行为并name没有延伸到其他测试中。使用正则表达式代替,问题就消失了:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

我想这可以被视为一个错误。有人愿意提交错误报告吗?我的版本是 find (GNU findutils) 4.6.0

答案4

假设文件 /john/paul/george/ringo/beatles 存在,并且您正在搜索的文件名为“stones”

find / stones

find 会将“beatles”与“stones”进行比较,并在“s”和“b”不匹配时将其删除。

find / | grep stones

在这种情况下,find 会将“/john/paul/george/ringo/beatles”传递给 grep,而 grep 在确定其是否匹配之前必须遍历整个路径。

因此 grep 做了更多的工作,这就是为什么它需要更长的时间

相关内容