【问题标题】:GNU find: when does the default action apply?GNU find:默认操作何时适用?
【发布时间】:2016-05-05 09:43:42
【问题描述】:

Debian 8 的 find 命令的手册页说:

如果整个表达式不包含除 -prune 或 -print 之外的任何操作, -print 对整个表达式为真的所有文件执行。

那么为什么这些输出不同:

$ mkdir -p test/foo test/bar && cd test && touch foo/bar bar/foo
$ # Test 1
$ find . -name foo -type d -prune -o -name foo
./foo
./bar/foo
$ # Test 2
$ find . -name foo -type d -prune -o -name foo -print
./bar/foo

所以测试 1:表达式是否包含“除了 -prune 或 -print 之外没有其他操作”?好吧,不包括修剪,是的,这句话是真的,没有任何行动。所以这些结果是预期的,因为对于./foo-o 选项之前的表达式返回 True,所以它被打印出来了。

但测试 2:该表达式是否包含“除了 -prune 或 -print 之外没有其他操作”?好吧,不包括修剪和印刷品,是的,这句话又是真的,没有其他动作。所以我希望得到同样的结果。

但我没有收到./foo。为什么?

就好像手册页应该这样写:“如果整个表达式不包含除 -prune 或 -print 之外的任何操作,则 -print 将对整个表达式为真的所有文件执行。 "

【问题讨论】:

  • 我认为它应该写成no actions other than -prune. 如果表达式中的某处已经有一个显式的-print,它肯定不会添加一个隐式的-print

标签: linux find gnu-findutils


【解决方案1】:

我将使用更简单的解释,手册页错误。它应该改为

如果整个表达式不包含除 -prune 或 -print 之外的任何操作,则对整个表达式为 true 的所有文件执行 -print。

它还应该包含对-quit 的警告,这是一个动作,但它会导致-find 立即退出。因此,即使为整个表达式添加了隐式 -print,它也不会真正执行。

posix find man page 包含更清晰的解释,尽管它没有扩展的 gnu 版本那么多的操作。

如果不存在表达式,则应使用-print 作为表达式。否则,如果给定的表达式不包含任何主要的 -exec、-ok 或 -print,则给定的表达式应有效地替换为:

(给定表达式)-打印

gnu 调用的动作中,posix 只定义了-exec-ok-print-prune。它没有任何扩展操作-delete-ls 等...因此定义与更正后的gnu 匹配,只需省略-prune

这里有一些使用 gnu find 动作的例子来证明这一点。对于所有考虑以下文件结构

$ tree
.
└── file

-删除

$ find -name file -delete
$

-执行命令;

$ find -name file -exec echo '-exec is an action so an implicit -print is not applied' \;
-exec is an action so an implicit -print is not applied
$

-execdir 命令{} +

$ find -name file -exec echo 'This should print the filename twice if an implicit -print is applied: ' {} +
This should print the filename twice if an implicit -print is applied:  ./file
$

-fls

$ find -name file -fls file
$

-fprint

$ find -name file -fprint file
$

-ls

$ find -name file -ls
1127767338    0 -rw-rw-r--   1 user   user          0 May  6 07:15 ./file
$

-ok 命令;

$ find -name file -ok echo '-ok is an action so an implicit -print is not applied' \;
< echo ... ./file > ? y
-ok is an action so an implicit -print is not applied
$

-okdir 命令;

$ find -name file -okdir echo '-okdir is an action so an implicit -print is not applied' \;
< echo ... ./file > ? y
-okdir is an action so an implicit -print is not applied
$

-打印

#./file would be printed twice if an implicit `-print was applied`
$ find -name file -print
./file
$

-print0

#./file would be printed twice if an implicit `-print was applied`
$ find -name file -print0
./file$

-printf

$ find -name file -printf 'Since -printf is an action the implicit -print is not applied\n'
Since -printf is an action the implicit -print is not applied
$

-修剪

$ find -name file -prune
./file
$

-退出

$ find -name file -quit
$ find -D opt -name file -quit
...
Optimized command line:
( -name file [0.1] -a [0.1] -quit [1]  ) -a [0.1] -print [1]

【讨论】:

  • 太棒了!喜欢详尽的例子。 find -name file -prune -o -prune 呢?这包含除-prune 之外的任何操作,所以应该打印,不是吗?但与find -name file -prune 不同,它不打印./file。文件匹配LHS-name-prune返回true,所以表达式用LHS解决,RHS应该没有效果。但显然确实如此。
  • @artfulrobot 它确实添加了隐含的-print,但它prunes 根目录.,所以它实际上从不下降到文件。即find -name file -prune -o ! -name '.' -prune
  • 但是它从左到右处理表达式不是吗?在这种情况下,文件./file 将永远不会达到第二次修剪,因为-o 左侧的初始匹配返回true,因此完成了表达式的评估。另外,我打印了.,而不是./file。 paste2.org/tvBcN5z7>
  • @artfulrobot 当然,但它根本不会处理./file。在该目录中只执行find。您将在第一行看到.,在第二行看到./file。现在执行find -prune。您将再次看到.,如您的示例所示。在您的示例中,. 与 LHS 不匹配,但它与 RHS 匹配,因此它只是像 find -prune 中那样被修剪
  • Aaaaohw,我听了!当然,因为 find 从顶部开始,根,所以正如你所说,它会被修剪,所以它永远不会看到文件。而且因为它只包含修剪动作,所以它被打印出来了。好的,谢谢!
【解决方案2】:

让我们看看这个命令:

find . -name foo -type d -prune -o -name foo

由于-print 是默认操作,因此该操作将应用于整个表达式集,即-name foo -type d -prune -o -name foo。所以和下面的一样:

find . \( -name foo -type d -prune -o -name foo \) -print

现在让我们看看这个命令:

find . -name foo -type d -prune -o -name foo -print

根据man findexpr1 expr2 的优先级高于expr1 -o expr2。所以在上面的命令中,两个表达式用 OR 运算符组合起来:

  • -name foo -type d -prune
  • -name foo -print

因此,如果您想将-print 应用于两者,请使用括号:

find . \( -name foo -type d -prune -o -name foo \) -print

但是-prune -o RHS 意味着RHS 仅针对那些没有被修剪的项目进行评估。

我们可以通过运行find-D tree-D opt 来检查我们是否正确:

find -D opt -O0 . -name foo -type d -prune -o -name foo -print
...
 (  ( -name foo [0.1] -a [0.04] [need type] -type d [0.4]  ) -a [0.04] [call stat] [need type] -prune [1]  ) -o [0.14]  ( -name foo [0.1] -a [0.1] -print [1]  ) 
./bar/foo


find -D opt -O0 . -name foo -type d -prune -o -name foo
 (  (  ( -name foo [0.1] -a [0.04] [need type] -type d [0.4]  ) -a [0.04] [call stat] [need type] -prune [1]  ) -o [1] -name foo [0.1]  ) -a [0.14] -print [1] 
./foo
./bar/foo

正如我们所见,find 从我们明确放置 -print 的第一个表达式生成 (... -prune) -o (... -print)。它从我们省略 -print 的第二个表达式生成 (...) -a -print

所以我认为手册页中的“整个表达式”是指OPERATORS 部分中描述的表达式部分之一。

【讨论】:

  • 我同意您的测试并理解它们。但这与手册页 AFAICS 不匹配。在您的段落开头“现在让我们看看...”中,您是说未应用默认操作,但是与手册页相比,这种情况如何,因为您的“整个表达式”不包含除 -prune 之外的任何操作和-print,因此应该应用默认值 - 根据该人的说法 - 应用?
  • @artfulrobot,也许,手册页需要澄清。但是,如果find 打印了所有内容,那将是毫无意义的。我们应该可以选择修剪一些东西,然后打印其余的东西。顺便说一下,手册页有一些关于这个问题的例子。例如:find . -path ./src/emacs -prune -o -print, "...跳过目录 `src/emacs' 及其下的所有文件和目录,并打印找到的其他文件的名称..."
  • 是的,谢谢,我认为你是对的:当手册页说“整个表达式”时,它的意思是“部分”!我认为 Ignacio 简洁明了,所以我接受了这个答案,但也非常感谢您的意见!
  • @RuslanOsmanov 在So the command above does the same as the following:... 之前回答得很好,然后就有点不清楚了。只有在原件左侧没有修剪任何内容时,它才会做同样的事情。
  • @BroSlow,我不同意。表达式\( -name foo -type d -prune -o -name foo \) 由 OR 分隔的两部分组成。在第一个 expr。 -prune 返回 True,如果它是一个名为“foo”的目录。第二个表达式。是True,如果名称是“foo”。所以整个表达式是True,如果名称是“foo”。然后该命令将-print 应用于组\( ... \)。所以该命令打印所有“foo”,与find . -name foo -print相同。
【解决方案3】:

Check the GNU Findutils manual,上面写着

如果表达式不包含“-prune”以外的任何操作,则“-print”是 对整个表达式为真的所有文件执行。

显然,debian's manual 是错误的,因为它只是一个 GNU 查找。而且我不知道为什么会这样,因为它对我来说只是一个副本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2018-12-08
    • 1970-01-01
    相关资源
    最近更新 更多