如何使用 xargs 复制名称中包含空格和引号的文件?
我正在尝试复制目录下的一堆文件,其中许多文件的名称中包含空格和单引号。 当我尝试将 find
和 grep
与 xargs
串在一起时,出现以下错误:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
对于更稳健地使用 xargs 有什么建议吗?
这是在 Mac OS X 10.5.3 (Leopard) 和 BSD xargs< /代码>。
I'm trying to copy a bunch of files below a directory and a number of the files have spaces and single-quotes in their names. When I try to string together find
and grep
with xargs
, I get the following error:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
Any suggestions for a more robust usage of xargs?
This is on Mac OS X 10.5.3 (Leopard) with BSD xargs
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(22)
您可以将所有这些组合到一个
find
命令中:这将处理其中包含空格的文件名和目录。 您可以使用
-name
来获取区分大小写的结果。注意:传递给
cp
的--
标志阻止它处理以-
开头的文件作为选项。You can combine all of that into a single
find
command:This will handle filenames and directories with spaces in them. You can use
-name
to get case-sensitive results.Note: The
--
flag passed tocp
prevents it from processing files starting with-
as options.<代码>查找 . -打印0 | grep --null 'FooBar' | grep --null 'FooBar' | grep --null 'FooBar' | grep --null 'FooBar' xargs -0 ...
我不知道
grep
是否支持--null
,也不知道xargs
是否支持-0
,在 Leopard 上,但在 GNU 上一切都很好。find . -print0 | grep --null 'FooBar' | xargs -0 ...
I don't know about whether
grep
supports--null
, nor whetherxargs
supports-0
, on Leopard, but on GNU it's all good.实现原始发布者想要的最简单方法是将分隔符从任何空格更改为行尾字符,如下所示:
The easiest way to do what the original poster wants is to change the delimiter from any whitespace to just the end-of-line character like this:
这更有效,因为它不会多次运行“cp”:
This is more efficient as it does not run "cp" multiple times:
我遇到了同样的问题。 我是这样解决的:
我使用 sed 将每一行输入替换为同一行,但用双引号引起来。 在
sed
手册页中,“...替换中出现的与号 (``&'') 被替换为与 RE 匹配的字符串...” -- 在本例中,.*
,整行。这解决了
xargs:未终止的引号
错误。I ran into the same problem. Here's how I solved it:
I used
sed
to substitute each line of input with the same line, but surrounded by double quotes. From thesed
man page, "...An ampersand (``&'') appearing in the replacement is replaced by the string matching the RE..." -- in this case,.*
, the entire line.This solves the
xargs: unterminated quote
error.此方法适用于 Mac OS X v10.7.5 (Lion):
我还测试了您发布的确切语法。 这在 10.7.5 上也运行良好。
This method works on Mac OS X v10.7.5 (Lion):
I also tested the exact syntax you posted. That also worked fine on 10.7.5.
只是不要使用
xargs
。 这是一个简洁的程序,但在面对重要情况时,它与find
的配合不太好。这是一个可移植的 (POSIX) 解决方案,即不需要
find
、xargs
或cp
GNU 特定扩展的解决方案:请注意结尾 < code>+ 而不是更常见的
;
。此解决方案:
正确处理带有嵌入空格、换行符或任何外来字符的文件和目录。
适用于任何 Unix 和 Linux 系统,甚至不提供 GNU 工具包的系统。
不使用
xargs
,这是一个很好且有用的程序,但需要太多调整和非标准功能才能正确处理find
输出。也比公认的和大多数(如果不是全部)其他答案更高效(阅读更快)。
另请注意,尽管在其他一些回复或评论中有所说明,但引用
{}
是没有用的(除非您使用的是异国情调的fish
shell)。Just don't use
xargs
. It is a neat program but it doesn't go well withfind
when faced with non trivial cases.Here is a portable (POSIX) solution, i.e. one that doesn't require
find
,xargs
orcp
GNU specific extensions:Note the ending
+
instead of the more usual;
.This solution:
correctly handles files and directories with embedded spaces, newlines or whatever exotic characters.
works on any Unix and Linux system, even those not providing the GNU toolkit.
doesn't use
xargs
which is a nice and useful program, but requires too much tweaking and non standard features to properly handlefind
output.is also more efficient (read faster) than the accepted and most if not all of the other answers.
Note also that despite what is stated in some other replies or comments quoting
{}
is useless (unless you are using the exoticfish
shell).对于那些依赖除 find 之外的命令的人,例如
ls
:For those who relies on commands, other than find, eg
ls
:考虑将 xargs 的 --null 命令行选项与 find 中的 -print0 选项一起使用。
Look into using the --null commandline option for xargs with the -print0 option in find.
我相信这对于除换行符之外的任何字符都可以可靠地工作(并且我怀疑如果你的文件名中有换行符,那么你会遇到比这更糟糕的问题)。 它不需要 GNU findutils,只需要 Perl,所以它应该可以在任何地方工作。
I believe that this will work reliably for any character except line-feed (and I suspect that if you've got line-feeds in your filenames, then you've got worse problems than this). It doesn't require GNU findutils, just Perl, so it should work pretty-much anywhere.
我发现以下语法对我来说效果很好。
在此示例中,我正在查找安装在“/usr/pcapps”的文件系统中超过 1,000,000 字节的最大 200 个文件。
“find”和“xargs”之间的 Perl 行衬线转义/引用每个空格,因此“xargs”将任何嵌入空格的文件名作为单个参数传递给“ls”。
I have found that the following syntax works well for me.
In this example, I am looking for the largest 200 files over 1,000,000 bytes in the filesystem mounted at "/usr/pcapps".
The Perl line-liner between "find" and "xargs" escapes/quotes each blank so "xargs" passes any filename with embedded blanks to "ls" as a single argument.
框架挑战 — 您询问如何使用 xargs。 答案是:你不使用 xargs,因为你不需要它。
user80168
的评论描述了一种直接使用 cp 执行此操作的方法,无需为每个文件调用 cp:这有效的原因是:
cp -t
标志允许在 cp 开头附近而不是末尾附近给出目标目录。 来自man cp
:--
标志告诉cp
将后面的所有内容解释为文件名,而不是标志,因此以-
或开头的文件>--
不要混淆cp
; 您仍然需要这个,因为-
/--
字符由cp
解释,而任何其他特殊字符由 shell 解释。find -exec command {} +
变体本质上与 xargs 相同。 来自man find
:通过直接在 find 中使用它,可以避免对管道或 shell 调用的需要,这样您就不必担心文件名中的任何讨厌的字符。
Frame challenge — you're asking how to use xargs. The answer is: you don't use xargs, because you don't need it.
The comment by
user80168
describes a way to do this directly with cp, without calling cp for every file:This works because:
cp -t
flag allows to give the target directory near the beginning ofcp
, rather than near the end. Fromman cp
:The
--
flag tellscp
to interpret everything after as a filename, not a flag, so files starting with-
or--
do not confusecp
; you still need this because the-
/--
characters are interpreted bycp
, whereas any other special characters are interpreted by the shell.The
find -exec command {} +
variant essentially does the same as xargs. Fromman find
:By using this in find directly, this avoids the need of a pipe or a shell invocation, such that you don't need to worry about any nasty characters in filenames.
使用 Bash(不是 POSIX),您可以使用进程替换来获取变量中的当前行。 这使您可以使用引号来转义特殊字符:
With Bash (not POSIX) you can use process substitution to get the current line inside a variable. This enables you to use quotes to escape special characters:
请注意,其他答案中讨论的大多数选项在不使用 GNU 实用程序的平台(例如 Solaris、AIX、HP-UX)上都不是标准的。 请参阅 POSIX 规范了解“标准”xargs 行为。
我还发现 xargs 的行为即使没有输入也至少运行一次命令,这很麻烦。
我编写了自己的 xargs (xargl) 私有版本来处理名称中的空格问题(仅换行符分隔 - 尽管“find ... -print0”和“xargs -0”组合非常简洁,因为文件名不能包含 ASCII NUL '\0' 字符 我的 xargl 并不完整,值得发布 - 特别是因为 GNU 拥有至少同样好的设施。
Be aware that most of the options discussed in other answers are not standard on platforms that do not use the GNU utilities (Solaris, AIX, HP-UX, for instance). See the POSIX specification for 'standard' xargs behaviour.
I also find the behaviour of xargs whereby it runs the command at least once, even with no input, to be a nuisance.
I wrote my own private version of xargs (xargl) to deal with the problems of spaces in names (only newlines separate - though the 'find ... -print0' and 'xargs -0' combination is pretty neat given that file names cannot contain ASCII NUL '\0' characters. My xargl isn't as complete as it would need to be to be worth publishing - especially since GNU has facilities that are at least as good.
对我来说,我试图做一些不同的事情。 我想将 .txt 文件复制到 tmp 文件夹中。 .txt 文件名包含空格和撇号字符。 这在我的 Mac 上有效。
For me, I was trying to do something a little different. I wanted to copy my .txt files into my tmp folder. The .txt filenames contain spaces and apostrophe characters. This worked on my Mac.
如果您系统上的 find 和 xarg 版本不支持
-print0
和-0
开关(例如 AIX find 和 xargs),您可以使用这个看起来很糟糕的代码:Here sed将负责转义 xargs 的空格和引号。
在 AIX 5.3 上测试
If find and xarg versions on your system doesn't support
-print0
and-0
switches (for example AIX find and xargs) you can use this terribly looking code:Here sed will take care of escaping the spaces and quotes for xargs.
Tested on AIX 5.3
我围绕“xargs”创建了一个名为“xargsL”的小型便携式包装脚本,它解决了大多数问题。
与 xargs 相反,xargsL 每行接受一个路径名。 路径名可以包含除(显然)换行符或 NUL 字节之外的任何字符。
文件列表中不允许或不支持引用 - 您的文件名可能包含各种空格、反斜杠、反引号、shell 通配符等 - xargsL 会将它们作为文字字符处理,不会造成任何损害。
作为一个额外的奖励功能,如果没有输入,xargsL 将不会运行命令一次!
请注意区别:
给予 xargsL 的任何参数都将传递给 xargs。
这是“xargsL”POSIX shell 脚本:
将脚本放入 $PATH 中的某个目录中,并且不要忘记
$ chmod +x xargsL
脚本以使其可执行。
I created a small portable wrapper script called "xargsL" around "xargs" which addresses most of the problems.
Contrary to xargs, xargsL accepts one pathname per line. The pathnames may contain any character except (obviously) newline or NUL bytes.
No quoting is allowed or supported in the file list - your file names may contain all sorts of whitespace, backslashes, backticks, shell wildcard characters and the like - xargsL will process them as literal characters, no harm done.
As an added bonus feature, xargsL will not run the command once if there is no input!
Note the difference:
Any arguments given to xargsL will be passed through to xargs.
Here is the "xargsL" POSIX shell script:
Put the script into some directory in your $PATH and don't forget to
$ chmod +x xargsL
the script there to make it executable.
bill_starr 的 Perl 版本 不适用于嵌入式换行符(仅处理空格)。 对于那些在例如 Solaris 上没有 GNU 工具的人,更完整的版本可能是(使用 sed)...
根据需要调整 find 和 grep 参数或其他命令,但 sed 将修复嵌入的换行符/空格/制表符。
bill_starr's Perl version won't work well for embedded newlines (only copes with spaces). For those on e.g. Solaris where you don't have the GNU tools, a more complete version might be (using sed)...
adjust the find and grep arguments or other commands as you require, but the sed will fix your embedded newlines/spaces/tabs.
我用了 Bill Star 的答案在 Solaris 上稍作修改:
这将在每行周围加上引号。 我没有使用“-l”选项,尽管它可能会有所帮助。
我要查看的文件列表可能有“-”,但没有换行符。 我没有将输出文件与任何其他命令一起使用,因为我想在开始通过 xargs 大规模删除它们之前查看发现的内容。
I used Bill Star's answer slightly modified on Solaris:
This will put quotes around each line. I didn't use the '-l' option although it probably would help.
The file list I was going though might have '-', but not newlines. I haven't used the output file with any other commands as I want to review what was found before I just start massively deleting them via xargs.
我玩了一下,开始考虑修改 xargs,并意识到对于我们在这里讨论的用例,用 Python 进行简单的重新实现是一个更好的主意。
一方面,整个事情有大约 80 行代码意味着很容易弄清楚发生了什么,如果需要不同的行为,您可以将其破解为新脚本,所需时间比获得所需的时间要短在 StackOverflow 等地方的回复。
请参阅 https://github.com/johnallsup/jda-misc-scripts /blob/master/yargs 和 https:/ /github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py。
使用 yargs 编写的(并且安装了 Python 3),您可以键入:
一次复制 203 个文件。 (当然,这里 203 只是一个占位符,使用像 203 这样的奇怪数字可以清楚地表明这个数字没有其他意义。)
如果你真的想要更快并且不需要 Python 的东西,可以将 zargs 和 yargs 作为原型用 C++ 或 C 重写。
I played with this a little, started contemplating modifying xargs, and realised that for the kind of use case we're talking about here, a simple reimplementation in Python is a better idea.
For one thing, having ~80 lines of code for the whole thing means it is easy to figure out what is going on, and if different behaviour is required, you can just hack it into a new script in less time than it takes to get a reply on somewhere like Stack Overflow.
See https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs and https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py.
With yargs as written (and Python 3 installed) you can type:
to do the copying 203 files at a time. (Here 203 is just a placeholder, of course, and using a strange number like 203 makes it clear that this number has no other significance.)
If you really want something faster and without the need for Python, take zargs and yargs as prototypes and rewrite in C++ or C.
您可能需要 grep Foobar 目录,例如:
You might need to grep Foobar directory like:
如果您使用的是 Bash,则可以通过
mapfile
将 stdout 转换为行数组:优点是:
您可以将其他参数附加到文件名中。 对于
cp
,您还可以:<前><代码>查找 . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
但是,某些命令没有这样的功能。
缺点:
好吧......谁知道 Bash 是否在 OS X 上可用?
If you are using Bash, you can convert stdout to an array of lines by
mapfile
:The benefits are:
You can append other arguments to the file names. For
cp
, you can also:however, some commands don't have such feature.
The disadvantages:
Well... who knows if Bash is available on OS X?