我试图复制一堆文件下面的目录和一些文件有空格和单引号在他们的名字。当我尝试用xargs将find和grep串在一起时,我得到以下错误:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
对于更健壮地使用xargs有什么建议吗?
这是在Mac OS X 10.5.3 (Leopard)上使用BSD xargs。
我试图复制一堆文件下面的目录和一些文件有空格和单引号在他们的名字。当我尝试用xargs将find和grep串在一起时,我得到以下错误:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
对于更健壮地使用xargs有什么建议吗?
这是在Mac OS X 10.5.3 (Leopard)上使用BSD xargs。
当前回答
框架挑战-你正在询问如何使用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调用的需要,这样您就不需要担心文件名中任何讨厌的字符。
其他回答
你可以把所有这些组合成一个find命令:
find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;
这将处理包含空格的文件名和目录。您可以使用name来获得区分大小写的结果。
注意:传递给cp的——标志阻止它处理以-开头的文件。
使用Bash(而不是POSIX),您可以使用进程替换来获取变量中的当前行。这允许你使用引号转义特殊字符:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
我尝试了一下,开始考虑修改xargs,并意识到对于我们在这里讨论的这种用例,用Python简单地重新实现是一个更好的主意。
首先,整个事情有大约80行代码意味着很容易弄清楚发生了什么,如果需要不同的行为,你可以在更短的时间内把它黑进一个新的脚本,而不是在Stack Overflow等地方得到回复。
参见https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs和https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py。
使用编写好的yargs(并且安装了Python 3),您可以键入:
find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar
一次拷贝203个文件。(这里203只是一个占位符,当然,使用像203这样奇怪的数字可以清楚地表明这个数字没有其他意义。)
如果你真的想要更快,不需要Python,可以把zargs和yargs作为原型,然后用c++或C重写。
这更有效,因为它不会多次运行“cp”:
find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
查看一下在xargs中使用——null命令行选项和find中的-print0选项。