我想知道{} \;
和{} \+
以及| 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'
为什么?任何人都可以解释我做错了什么?
答案 0 :(得分:181)
manual page(或online GNU manual)几乎解释了一切。
对于每个结果,执行command {}
。 {}
的所有出现都被文件名替换。 ;
以斜杠为前缀,以防止shell解释它。
每个结果都附加到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]'
,我们还避免依赖当前区域设置来决定c
或p
的大写版本。
请注意,与+
相反的;
在任何外壳中并不特殊,因此不需要引用(虽然引用不会造成伤害,当然除了像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我很长一段时间都没有用过,但我觉得像+。