我试图复制一堆文件下面的目录和一些文件有空格和单引号在他们的名字。当我尝试用xargs将find和grep串在一起时,我得到以下错误:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

对于更健壮地使用xargs有什么建议吗?

这是在Mac OS X 10.5.3 (Leopard)上使用BSD xargs。


当前回答

bill_starr的Perl版本不能很好地用于嵌入换行符(只处理空格)。对于那些没有GNU工具的Solaris,一个更完整的版本可能是(使用sed)…

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

根据需要调整find和grep参数或其他命令,但sed将修复嵌入的换行符/空格/制表符。

其他回答

框架挑战-你正在询问如何使用xargs。答案是:不使用xargs,因为不需要它。

user80168的注释描述了一种直接使用cp的方法,而不需要对每个文件调用cp:

find . -name '*FooBar*' -exec cp -t /tmp -- {} +

这很有效,因为:

cp -t标志允许在靠近cp开头的位置给出目标目录,而不是靠近结束的位置。来自男人cp:

- t,目标目录=目录 将所有SOURCE参数复制到DIRECTORY中

——标志告诉cp将后面的所有内容解释为文件名,而不是标志,因此以-或——开头的文件不会混淆cp;您仍然需要这样做,因为-/——字符由cp解释,而任何其他特殊字符由shell解释。 find -exec命令{}+变体本质上与xargs相同。从人类发现:

-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 invoca‐ 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, and (when find is being invoked from a shell) it should be quoted (for example, '{}') to protect it from interpretation by shells. The command is executed in the starting directory. If any invocation returns a non-zero value as exit status, then find returns a non-zero exit status. If find encounters an error, this can sometimes cause an immedi‐ ate exit, so some pending commands may not be run at all. This variant of -exec always returns true.

通过直接在find中使用它,就避免了管道或shell调用的需要,这样您就不需要担心文件名中任何讨厌的字符。

只是不要使用xargs。这是一个简洁的程序,但当面对非平凡的情况时,它不太适合find。

这是一个可移植的(POSIX)解决方案,即不需要find, xargs或cp GNU特定的扩展:

find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +

注意结尾的+而不是更常见的;。

这个解决方案:

正确处理带有嵌入空格、换行符或任何外来字符的文件和目录。 适用于任何Unix和Linux系统,即使是那些没有提供GNU工具包的系统。 不使用xargs,这是一个很好的有用的程序,但需要太多的调整和非标准特性来正确处理查找输出。 也比公认的和大多数(如果不是全部的话)其他答案更有效(读得更快)。

还要注意,尽管在其他一些回复或评论中声明了什么,引用{}是无用的(除非您使用的是异国情调的fishshell)。

bill_starr的Perl版本不能很好地用于嵌入换行符(只处理空格)。对于那些没有GNU工具的Solaris,一个更完整的版本可能是(使用sed)…

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

根据需要调整find和grep参数或其他命令,但sed将修复嵌入的换行符/空格/制表符。

此方法适用于Mac OS X v10.7.5 (Lion):

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

我还测试了你发布的确切语法。这在10.7.5上也能正常工作。

find | perl -lne 'print quotemeta' | xargs ls -d

我相信这对于除换行以外的任何字符都是可靠的(并且我怀疑如果您的文件名中有换行,那么您将遇到比这更糟糕的问题)。它不需要GNU findutils,只需要Perl,所以它应该可以在任何地方工作。