Linux 为什么 find -exec mv {} ./target/ + 不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5607542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does find -exec mv {} ./target/ + not work?
提问by Shahadat Hossain
I want to know exactly what {} \;
and {} \+
and | xargs ...
do. Please clarify these with explanations.
我想知道到底是什么{} \;
和{} \+
和| xargs ...
做的。请用解释来澄清这些。
Below 3 commands run and output same result but the first command takes a little time and the format is also little different.
下面 3 个命令运行并输出相同的结果,但第一个命令需要一点时间,格式也略有不同。
find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file
It's because 1st one runs the file
command for every file coming from the find
command. So, basically it runs as:
这是因为第一个file
为来自命令的每个文件运行find
命令。所以,基本上它运行为:
file file1.txt
file file2.txt
But latter 2 find with -exec
commands run file command once for all files like below:
但是后 2 个 find-exec
命令为所有文件运行一次文件命令,如下所示:
file file1.txt file2.txt
Then I run the following commands on which first one runs without problem but second one gives error message.
然后我运行以下命令,第一个运行没有问题,但第二个给出错误消息。
find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'
For command with {} \+
, it gives me the error message
对于命令 with {} \+
,它给了我错误信息
find: missing argument to `-exec'
why is that? can anyone please explain what am I doing wrong?
这是为什么?谁能解释一下我做错了什么?
采纳答案by Lekensteyn
The manual page(or the online GNU manual) pretty much explains everything.
find -exec command {} \;
find -exec 命令 {} \;
For each result, command {}
is executed. All occurences of {}
are replaced by the filename. ;
is prefixed with a slash to prevent the shell from interpreting it.
对于每个结果,command {}
都会执行。所有出现的{}
都被文件名替换。;
以斜杠为前缀,以防止 shell 解释它。
find -exec command {} +
find -exec 命令 {} +
Each result is appended to command
and executed afterwards. Taking the command length limitations into account, I guess that this command may be executed more times, with the manual page supporting me:
每个结果都附加到command
并在之后执行。考虑到命令长度限制,我猜这个命令可能会执行更多次,手册页支持我:
the total number of invocations of the command will be much less than the number of matched files.
命令的总调用次数将远小于匹配文件的数量。
Note this quote from the manual page:
请注意手册页中的引用:
The command line is built in much the same way that xargs builds its command lines
命令行的构建方式与 xargs 构建其命令行的方式大致相同
That's why no characters are allowed between {}
and +
except for whitespace. +
makes find detect that the arguments should be appended to the command just like xargs
.
这就是为什么没有字符之间允许{}
和+
除空白。+
使 find 检测到参数应该像xargs
.
The solution
解决方案
Luckily, the GNU implementation of mv
can accept the target directory as an argument, with either -t
or the longer parameter --target
. It's usage will be:
幸运的是,GNU 实现mv
可以接受目标目录作为参数,带有-t
或更长的参数--target
。它的用法是:
mv -t target file1 file2 ...
Your find
command becomes:
你的find
命令变成:
find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+
From the manual page:
从手册页:
-exec command ;
Execute command; true if 0 status is returned. All following arguments to find are taken to be arguments to the command until an argument consisting of `;' is encountered. The string `{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a `\') or quoted to protect them from expansion by the shell. See the EXAMPLES section for examples of the use of the -exec option. The specified command is run once for each matched file. The command is executed in the starting directory. There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.
-exec command {} +
This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invocations of the command will be much less than the number of matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of `{}' is allowed within the command. The command is executed in the starting directory.
-exec 命令;
执行命令;如果返回 0 状态,则为 true。find 的所有以下参数都被视为命令的参数,直到参数包含“;” 遇到了。字符串“{}”被当前正在处理的文件名替换,它出现在命令的参数中的任何地方,而不仅仅是在某些版本的 find 中单独出现在参数中。这两种结构都可能需要转义(使用“\”)或引用以防止它们被 shell 扩展。有关使用 -exec 选项的示例,请参见示例部分。指定的命令对每个匹配的文件运行一次。该命令在起始目录中执行。使用 -exec 操作存在不可避免的安全问题;您应该改用 -execdir 选项。
-exec 命令 {} +
-exec 操作的这种变体对选定的文件运行指定的命令,但是命令行是通过在每个选定的文件名后附加来构建的;命令的总调用次数将远小于匹配文件的数量。命令行的构建方式与 xargs 构建其命令行的方式非常相似。命令中只允许出现一个“{}”实例。该命令在起始目录中执行。
回答by Mike Ramirez
no, the difference between +
and \;
should be reversed. +
appends the files to the end of the exec command then runs the exec command and \;
runs the command for each file.
没有,之间的区别+
,并\;
应调换。 +
将文件附加到 exec 命令的末尾,然后运行 exec 命令并\;
为每个文件运行该命令。
The problem is find . -type f -iname '*.cpp' -exec mv {} ./test/ \+
should be find . -type f -iname '*.cpp' -exec mv {} ./test/ +
no need to escape it or terminate the +
问题是find . -type f -iname '*.cpp' -exec mv {} ./test/ \+
应该find . -type f -iname '*.cpp' -exec mv {} ./test/ +
不需要逃避它或终止+
xargs I haven't used in a long time but I think works like +.
xargs 我很久没用了,但我认为像 +.
回答by arvymetal
I encountered the same issue on Mac OSX, using a ZSHshell: in this case there is no -t
option for mv
, so I had to find another solution.
However the following command succeeded:
我在Mac OSX上遇到了同样的问题,使用ZSHshell:在这种情况下,没有-t
选项mv
,所以我不得不找到另一个解决方案。但是下面的命令成功了:
find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;
The secret was to quote the braces. No need for the braces to be at the end of the exec
command.
秘诀是引用大括号。大括号不需要在exec
命令的末尾。
I tested under Ubuntu 14.04(with BASHand ZSHshells), it works the same.
我在Ubuntu 14.04(使用BASH和ZSHshell)下进行了测试,它的工作原理是一样的。
However, when using the +
sign, it seems indeed that it has to be at the end of the exec
command.
但是,在使用+
符号时,似乎确实必须在exec
命令的末尾。
回答by Stephane Chazelas
The standard equivalent of find -iname ... -exec mv -t dest {} +
for find
implementations that don't support -iname
or mv
implementations that don't support -t
is to use a shell to re-order the arguments:
标准等同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 {} +
By using -name '*.[cC][pP][pP]'
, we also avoid the reliance on the current locale to decide what's the uppercase version of c
or p
.
通过使用-name '*.[cC][pP][pP]'
,我们还避免了依赖当前语言环境来决定c
or的大写版本p
。
Note that +
, contrary to ;
is not special in any shell so doesn't need to be quoted (though quoting won't harm, except of course with shells like rc
that don't support \
as a quoting operator).
请注意+
,与;
在任何 shell 中都不是特殊的,因此不需要引用(尽管引用不会造成伤害,当然,像rc
这样的shell不支持\
作为引用运算符)。
The trailing /
in /dest/dir/
is so that mv
fails with an error instead of renaming foo.cpp
to /dest/dir
in the case where only one cpp
file was found and /dest/dir
didn't exist or wasn't a directory (or symlink to directory).
尾随/
的/dest/dir/
是,这样mv
因错误而失败,而不是重命名foo.cpp
到/dest/dir
的情况下只有一个cpp
文件被发现,/dest/dir
不存在或不是一个目录(或符号连接到目录)。
回答by DarkLabs
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+