在find中使用分号(;)与加号(+)和exec


159

为什么使用之间的输出有所不同

find . -exec ls '{}' \+

find . -exec ls '{}' \;

我有:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1

Answers:


249

最好用一个例子来说明。假设find打开了这些文件:

file1
file2
file3

使用-exec用分号(find . -exec ls '{}' \;),将执行

ls file1
ls file2
ls file3

但是,如果您使用加号代替(find . -exec ls '{}' \+),则将尽可能多的文件名作为参数传递给单个命令:

ls file1 file2 file3

文件名的数量仅受系统最大命令行长度的限制。如果命令超过此长度,则该命令将被多次调用。


1
谢谢。这对于希望对结果文件进行排序非常有用:find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas

1
傻瓜q:我注意到与+关联-exec总是被转义,但是+与关联-mtime却不是。你知道原因吗?我猜这是逃避;与关联的习惯-exec
kevinarpe

3
@kevinarpe确实,我会总结一下,以习惯于;。无法想象有必要逃脱+
马丁

36

到目前为止,所有答案都是正确的。我提供此信息是(对我来说)使用echo而不是来更清楚地说明所描述的行为ls

对于分号,echo每个找到的文件(或其他文件系统对象)将调用一次命令:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

带加号的命令echo仅被调用一次。找到的每个文件都作为参数传递。

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

如果find出现大量结果,您可能会发现该命令在参数数量上被阻塞。


1
难道不应该只将结果添加到可以安全地传递到外壳程序的数字上吗?至少是怎么 xargs做的...从原则上讲,它永远不会阻塞太多的争论。
Rmano

1
@Rmano:我已经看到在Solaris上find(和xargs)发出的参数过多,无法使用。GNU的findutils中xargs(和find)似乎表现得更为明智,但并非所有人都使用GNU。
Johnsyweb

2
@Johnsyweb,所有POSIX find都将尝试避免达到参数数量的限制。包括Solaris'(至少10个)。如果您做类似find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +或的事情find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +,它可能会失败,但find不能为此而怪罪。请注意,GNU find是最后find要支持的实现之一+(曾经是将脚本移植到GNU系统的一种痛苦)。
Stephane Chazelas 2014年

19

来自man find

-exec命令;

执行命令;如果返回0状态,则为true。后面的所有要查找的参数都将被视为命令的参数,直到由';'组成的参数为止 遇到。字符串'{}'被当前文件名替换,该文件名在命令的参数中出现的所有位置(而不仅仅是在单独的参数中,如在find的某些版本中)都在处理。这两种构造都可能需要转义(带有'\')或加引号,以防止它们被外壳扩展。有关使用'-exec'选项的示例,请参见示例秒部分。对于每个匹配的文件,指定的命令运行一次。 该命令在起始目录中执行。围绕-exec选项的使用存在不可避免的安全性问题。

-exec命令{} +

-exec选项的此变体在选定的文件上运行指定的命令,但是通过在末尾附加每个选定的文件名来构建命令行;该命令的调用总数将远远少于匹配文件的数目。命令行的构建与xargs构建命令行的方式几乎相同。命令中仅允许使用一个'{}'实例。该命令在起始目录中执行。

因此,据我了解,它对所\;找到的每个文件执行单独的命令find,而\+追加文件并在所有文件上执行单​​个命令。该\是一个转义字符,所以它是:

ls testdir1; ls testdir2 

ls testdir1 testdir2

在我的shell中执行上述操作即可反映您问题中的输出。

您何时要使用的示例 \+

假设有两个文件,1.tmp2.tmp

1.tmp:

1
2
3

2.tmp:

0
2
3

\;

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

而如果您使用\+(连接的结果find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

因此,在这种情况下diff 1.tmp; diff 2.tmpdiff 1.tmp 2.tmp

在某些情况下\;适当且\+必要。使用\+with rm是这样的一种情况,如果要删除大量文件,性能(速度)将优于\;


我也可以阅读手册页。而且我做到了,但我认为我不理解使用;之间的区别。vs +
Ankur Agarwal

我认为-1不公平,我解释了我对男人的理解。我不只是抄袭那个人然后离开。但我已经修改了回复,以包含一个更好的示例。
马修

10

find具有特殊的语法。您按{}原样使用,因为它们的含义是找到所找到文件的路径名,并且(大多数)shell不会以其他方式解释它们。您需要反斜杠,\;因为分号对shell有意义,它在find获得外壳之前先将其吃光了。因此find,在传递给C程序的参数列表中,要看完外壳之后要看的是:

“ -exec”,“ rm”,“ {}”,“;”

但是您需要\;在命令行上通过外壳将分号传递给参数。

您可以避免,\{\}因为shell引用的解释\{\}是just {}。同样,您可以使用'{}'。

不能做的就是使用

 -exec 'rm {} ;'

因为外壳程序将其解释为一个参数,

“ -exec”,“ rm {};”

rm {} ;不是命令的名称。(至少除非有人真的搞砸了。)

更新资料

区别在于

$ ls file1
$ ls file2

$ ls file1 file2

+被catenating名称到一个命令行。


1
我明白你在说什么。我问的是使用之间有什么区别?vs +
Ankur Agarwal

1
对不起,您是否仔细阅读了我的问题或评论?可能我需要改一下。为什么在find中将exec与分号一起使用时与在find中将exec与加号一起使用时,为什么会有不同的o / p?
阿加瓦尔

2
这是对命令为何如此解释的一个很好的解释,接受的答案没有涵盖。谢谢!

1

;(分号)或+(加号)之间的区别在于参数如何传递到find的-exec/ -execdir参数中。例如:

  • using ;将执行多个命令(每个参数分别执行),

    例:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    后面的所有自变量find均作为命令的自变量。

    该字符串{}将替换为当前正在处理的文件名。

  • using +将执行最少的命令(因为参数组合在一起)。这与xargs命令的工作原理非常相似,因此它将在每个命令中使用尽可能多的参数,以避免超出每行参数的最大限制。

    例:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    通过在每个末尾附加每个选定的文件名来构建命令行。

    命令中仅{}允许的一个实例。

也可以看看:


-5

我们正在尝试查找整理文件。

找 。-exec echo {} \; 该命令整夜都没有结果。

找 。-exec echo {} \ +具有结果,仅花费了几个小时。

希望这可以帮助。


2
这个答案没有解释这两种方式是如何工作的以及它们产生的结果如何不同。
misko321
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.