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

Sha*_*ain 96 linux cygwin exec find

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

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

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file
Run Code Online (Sandbox Code Playgroud)

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

file file1.txt
file file2.txt
Run Code Online (Sandbox Code Playgroud)

但后两个-exec命令运行文件命令一次为所有文件,如下所示:

file file1.txt file2.txt
Run Code Online (Sandbox Code Playgroud)

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

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'
Run Code Online (Sandbox Code Playgroud)

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

find: missing argument to `-exec'
Run Code Online (Sandbox Code Playgroud)

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

Lek*_*eyn 183

手册(或在线GNU手册)几乎说明了一切.

find -exec command {} \;

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

find -exec command {} +

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

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

请注意手册页中的引用:

命令行的构建方式与xargs构建命令行的方式大致相同

这就是为什么在空白之间{}+空白之间不允许使用任何字符的原因.+使find检测到参数应该附加到命令就像xargs.

解决方案

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

mv -t target file1 file2 ...
Run Code Online (Sandbox Code Playgroud)

你的find命令变为:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+
Run Code Online (Sandbox Code Playgroud)

从手册页:

-exec命令;

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

-exec命令{} +

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

  • `+` 命令有点奇怪 AFAIU,因为它将文件粘贴在“末尾”(而不是代替 `{}`),所以为什么要使用 `{}` - 这很令人困惑。感谢我不知道的“-t”选项,似乎该选项是作为“-exec +”问题的解决方法而创建的! (2认同)

arv*_*tal 6

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

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;
Run Code Online (Sandbox Code Playgroud)

秘密是引用括号.不需要大括号就在exec命令的末尾.

我在Ubuntu 14.04下测试(使用BASHZSH shell),它的工作原理相同.

但是,在使用+符号时,似乎确实必须在exec命令的末尾.


Ste*_*las 5

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

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +
Run Code Online (Sandbox Code Playgroud)

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

请注意+, 相反;在任何 shell 中都不是特殊的,因此不需要引用(尽管引用不会造成损害,当然除了像这样的 shellrc不支持\作为引用运算符)。

尾随//dest/dir/因为在只找到一个文件且不存在或不是目录(或目录的符号链接)的情况下mv会因错误而失败,而不是重命名为foo.cpp/dest/dircpp/dest/dir