为什么找-exec mv {} ./target/ +不起作用?

时间:2011-04-09 20:05:57

标签: linux find cygwin exec

我想知道{} \;{} \+以及| xargs ...到底做了什么。请用解释澄清这些。

以下3个命令运行并输出相同的结果,但第一个命令需要一点时间,格式也略有不同。

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

这是因为第一个命令为来自file命令的每个文件运行find命令。所以,基本上它运行如下:

file file1.txt
file file2.txt

但后两个用-exec命令查找对所有文件运行一次文件命令:

file file1.txt file2.txt

然后我运行以下命令,第一个运行没有问题,但第二个给出错误消息。

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

对于{} \+的命令,它会给我错误消息

find: missing argument to `-exec'

为什么?任何人都可以解释我做错了什么?

5 个答案:

答案 0 :(得分:181)

manual page(或online GNU manual)几乎解释了一切。

find -exec command {} \;

对于每个结果,执行command {}{}的所有出现都被文件名替换。 ;以斜杠为前缀,以防止shell解释它。

find -exec command {} +

每个结果都附加到command并在之后执行。考虑到命令长度限制,我猜这个命令可能会被执行多次,手册页支持我:

  

命令的调用总数将远远少于匹配文件的数量。

请注意手册页中的引用:

  

命令行的构建方式与xargs构建命令行的方式非常相似

这就是除了空格之外,{}+之间不允许使用任何字符的原因。 +使find检测到参数应该像xargs一样附加到命令。

解决方案

幸运的是,mv的GNU实现可以接受目标目录作为参数,使用-t或更长的参数--target。它的用法是:

mv -t target file1 file2 ...

您的find命令变为:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

从手册页:

  

-exec command;

     

执行命令;如果返回0状态,则返回true。查找的所有后续参数都被视为命令的参数,直到由`;'组成的参数为止遇到了。字符串`{}'被在命令参数中出现的任何位置处理的当前文件名替换,而不仅仅是在某些版本的find中的参数中。这两种结构都可能需要进行转义(使用“\”)或引用以保护它们不被shell扩展。有关使用-exec选项的示例,请参阅“示例”部分。为每个匹配的文件运行一次指定的命令。该命令在起始目录中执行。围绕使用-exec操作存在不可避免的安全问题;你应该使用-execdir选项。

     

-exec command {} +

     

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

答案 1 :(得分:5)

我使用 ZSH shell在 Mac OSX 上遇到了同样的问题:在这种情况下,-t没有mv选项,所以我必须找到另一个解决方案。 但是,以下命令成功:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

秘密是引用大括号。不需要将括号放在exec命令的末尾。

我在 Ubuntu 14.04 (使用 BASH ZSH shell)下测试过,它的工作原理相同。

然而,当使用+符号时,它似乎确实必须位于exec命令的末尾。

答案 2 :(得分:3)

不支持find -iname ... -exec mv -t dest {} +find不支持-iname实施的mv实施的标准等效-t是使用shell来重新排序参数:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

通过使用-name '*.[cC][pP][pP]',我们还避免依赖当前区域设置来决定cp的大写版本。

请注意,与+相反的;在任何外壳中并不特殊,因此不需要引用(虽然引用不会造成伤害,当然除了像shell一样不支持rc作为报价运营商的\

/中的结尾/dest/dir/mv因错误而失败,而不是在只有一个foo.cpp的情况下将/dest/dir重命名为cpp找到了1}}文件且/dest/dir没有存在或者不是目录(或目录的符号链接)。

答案 3 :(得分:0)

find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+

答案 4 :(得分:-1)

不,+\;之间的差异应该相反。 +将文件附加到exec命令的末尾,然后运行exec命令,\;运行每个文件的命令。

问题是find . -type f -iname '*.cpp' -exec mv {} ./test/ \+find . -type f -iname '*.cpp' -exec mv {} ./test/ +无需转义或终止+

xargs我很长一段时间都没有用过,但我觉得像+。