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


10

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

什么是对此行为的适当解释?


2
您将使用find列出每个文件,然后将数据传递给grep进行处理。单独使用find时,您缺少将每个列出的文件传递给grep来解析输出的步骤。因此,这将更快。
拉曼Sailopal

在什么意义上更慢?命令是否需要花费不同的时间来完成?
库萨兰达

1
我无法在本地复制。如果有的话,time find "$HOME" -name '.profile'报告的时间比更长time find "$HOME" | grep -F '.profile'。(17秒对12秒)。
库萨兰达

2
@JenniferAnderson我两次都跑了。17和12秒是平均值。是的,grep变化将在find结果中的任何地方匹配,而与匹配find -name将仅完全匹配(在这种情况下)。
库萨兰达

2
是的,find filename 很快。我有点认为这是错别字,而OP的意思是find -name filename。使用find filename,仅filename会被检查(没有其他检查)。
库萨兰达

Answers:


11

(我在find这里假设GNU )

仅使用

find filename

这样会很快,因为它只会返回filenamefilename如果是目录则返回内部名称,如果当前目录中不存在该名称,则会返回错误。这是一个非常快速的操作,类似于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

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

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

1
find filename只有将返回filename如果filename没有类型的目录(或为类目录,但没有任何条目本身)
斯特凡Chazelas

2

非技术性的解释:在人群中寻找杰克要比在人群中寻找所有人快得多,而除了杰克之外,所有人都无需考虑。


问题在于,OP希望Jack成为人群中唯一的人。如果是这样,他们很幸运。 find jack将列出jack是否是名为的文件jack,如果是目录,则列出目录中的所有名称。这是对find工作原理的误解。
库萨兰达

1

我还不了解这个问题,但是可以提供更多的见解。

就像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如果写的很少,您可能会认为这是胜利。但是ist变得更加尴尬了find。即使对于200K文件(13M的管道数据)根本没有要写入的内容,它也会丢失grep

time find /usr -name lwevhewoivhol

find可以以最快的速度grep,虽然

事实证明,find的愚蠢name并没有扩展到其他测试。改用正则表达式,问题就消失了:

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

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

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


您的时间安排有多可重复?如果您-name先进行测试,则由于目录内容未缓存,它可能会变慢。(在测试时-name-regex我发现它们大约花费相同的时间,至少在考虑了缓存效果之后。当然,它可能只是find... 的不同版本。)
psmears

@psmears当然,我已经多次进行了这些测试。甚至在第一个答案之前的问题注释中也提到了缓存问题。我的find版本是find(GNU findutils)4.6.0
Hauke Laging,

为什么添加-name '*.txt'速度变慢令人惊讶find?它必须做额外的工作,测试每个文件名。
巴玛(Barmar)'17年

@Barmar一方面,这项额外的工作可以非常快地完成。另一方面,这项额外的工作可以节省其他工作。find必须写更少的数据。写入管道的操作要慢得多。
Hauke Laging,

写入磁盘非常慢,写入管道还不错,它只是复制到内核缓冲区。请注意,在您的第一个测试中,编写更多内容以/dev/null某种方式花费了更少的系统时间。
Barmar

0

注意:我假设您的意思是find . -name filename(否则,您正在寻找不同的东西;find filename实际上是查找名为filename的路径,该路径可能几乎不包含文件,因此会很快退出)。


假设您有一个包含五千个文件的目录。在大多数文件系统上,这些文件实际上存储在结构中,该结构允许快速定位任何给定的文件。

所以,当你问find到找到他的名字只需要检查文件,find要求对于文件,只有文件,到底层的文件系统,这将读取的大容量存储很少的网页。因此,如果文件系统值得使用,那么此操作将比遍历整个树以检索所有条目快得多。

find但是,当您要求进行简单操作时,便会遍历整棵树并阅读。每一个 单。条目。对于大目录,这可能是一个问题(这正是为什么一些需要在磁盘上存储大量文件的软件会创建“目录树”深两三个组成部分的原因:这样,每个叶子只需要容纳更少的内容文件)。


-2

假设文件/ john / paul / george / ringo / beatles存在,并且您要搜索的文件称为“石头”

find / stones

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

find / | grep stones

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

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


1
你试过了吗?
Hauke Laging,

3
字符串比较的成本(极其简单和廉价)与目录查找的IO成本(或者如果是缓存,仅是syscall)完全相形见.。
马太福音

grep不是字符串比较,而是它的正则表达式比较,这意味着它必须遍历整个字符串,直到找到匹配项或到达末尾为止。无论如何,目录查找都是相同的。
偏执狂

@Paranoid Hm,您在说什么版本的find?显然,这与我在debian中所使用的发现不一样。
管道
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.