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


98

我想知道到底是什么{} \;{} \+| xargs ...做的。请通过解释加以澄清。

下面的3条命令运行并输出相同的结果,但第一个命令花费一些时间,格式也几乎没有差异。

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

这是因为第1个file命令针对来自该find命令的每个文件运行该命令。因此,基本上它的运行方式为:

file file1.txt
file file2.txt

但是后2个带有-exec命令的find 命令对所有文件运行一次run file命令,如下所示:

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'

这是为什么?谁能解释我在做什么错?


真正的问题很简单,为什么第一个有效而第二个无效?(1)找到。-type f -iname'.cpp'-exec mv {} ./test/ \; (2)找到。型f -iname'.cpp'
Shahadat Hossain

Answers:


185

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

找到-exec命令{} \;

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

查找-exec命令{} +

每个结果都将附加command并随后执行。考虑到命令长度的限制,我猜想该命令可能会执行更多次,其中手册页支持我:

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

请注意手册页中的以下引用:

命令行的构建与xargs构建命令行的方式几乎相同

这就是为什么在空格之间{}以及+除空格之外都不允许使用任何字符的原因。+make find可以检测到参数应附加到命令,就像xargs

解决方案

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

mv -t target file1 file2 ...

您的find命令将变为:

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

从手册页:

-exec命令;

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

-exec命令{} +

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


1
我确实知道它是如何工作的,我已经多次阅读了本手册,但是尽管{{ 我在Windows中使用Cygwin。
Shahadat Hossain

1
@Shahadat:您阅读过“从手册页开始”之前的部分吗?您已./test/{}和之间放置了+,但这些之间不允许有非空白字符。
Lekensteyn 2011年

茹说,我不应该在{}和+之间插入./test/。然后mv命令将如何工作;mv需要的源是{},而目标则是./test/,并以+结尾。您能把您认为正确的命令写出来吗?
Shahadat Hossain

@Shahadat:我明白你要达到的目标。Windows执行程序的速度很慢,因此您希望将其组合为一个命令。我将为答案添加替代方法。
Lekensteyn 2011年

1
+命令有点奇怪,因为AFAIU会将文件粘贴在“末尾”(而不是{}),所以为什么要使用它{}-这很令人困惑。感谢您-t不知道的选项,看来该选项是针对该-exec +问题的解决方法!
e2-e4

6

我在Mac OSX上使用ZSH shell 遇到了相同的问题:在这种情况下,没有-t选项mv,因此我不得不寻找另一种解决方案。但是,以下命令成功完成:

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

秘密在于引用括号。无需将花括号放在结尾处exec命令。

我在Ubuntu 14.04下进行了测试(使用BASHZSH外壳),其工作原理相同。

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


{}需要在被引用fishrc壳,但不是在zshbash也没有Bourne或CSH家庭的任何其他炮弹。
Stephane Chazelas

@StephaneChazelas是,在Ubuntu下用进行了重新测试bash,的确不需要引号。奇怪的是,如果没有在MacOS(使用zsh)下引用它们,我就会遇到问题。但是我没有Mac可以再试一次……
arvymetal

3

标准等同find -iname ... -exec mv -t dest {} +find不支持实现-inamemv不支持的实现-t是使用一个外壳来重新排序的参数:

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

通过使用-name '*.[cC][pP][pP]',我们还避免了依赖当前语言环境来确定cor 的大写版本p

请注意+,与相对的;在任何shell中都不是特殊的,因此不需要用引号引起来(尽管引号不会造成损害,除非对于rc不支持此类的shell\作为引号运算符)。

尾随//dest/dir/是,这样mv因错误而失败,而不是重命名foo.cpp/dest/dir的情况下只有一个cpp文件被发现,/dest/dir不存在或不是一个目录(或符号连接到目录)。


+1 ...作为执行命令的前提,进行shell内操作实际上对各种用例都非常有用...很好。
Cbhihe

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

请为您的答案添加一些解释,以便其他人可以从中学习
Nico Haase

您需要回答这个问题,需要解释。仅代码不是答案。
Lajos Arpad

-1

没有,之间的区别+,并\;应调换。 +将文件追加到exec命令的末尾,然后运行exec命令并\;为每个文件运行该命令。

问题是find . -type f -iname '*.cpp' -exec mv {} ./test/ \+应该find . -type f -iname '*.cpp' -exec mv {} ./test/ + 不必逃脱它或终止+

xargs我已经很久没有使用了,但是我认为它的作用类似于+。


我也尝试过这样做,但是得到了相同的错误消息。而且,在所有我发现只使用+的地方,但是在我的Cygwin中,我必须使用\ +或“ +”来工作。
Shahadat Hossain

哦,这是一个Cygwin环境。抱歉,我不知道,我不使用cygwin shell,我只使用* nix。
Mike Ramirez,

1
@Shahadat侯赛因试试-name "*.cpp"我几乎不使用-iname,除非我想要做一些高难度的正则表达式搜索,像-iname“???工作* \ CPP。”
迈克·拉米雷斯

1
@Mike:我认为您误解了-iname和之间的区别-name-iname是不区分大小写的版本-name,在正则表达式的处理上没有区别。我建议在发布之前尝试命令,您的命令在我的shell中也会失败。
Lekensteyn 2011年

1
@Lekensteyn在您发表评论之前已经确定了这种情况。我以为在您发表文章之前就已经承认Shahadat,这很简单。不,我没有手动运行它,而是从头顶上完成的,很少将这种形式的正则表达式与find一起使用。这只是一个“可能帮助”类型的事情。
Mike Ramirez
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.