我试图复制一堆文件的下一个目录和数量的文件具有空间和单一的报价在他们的名字。当我想要串起来 findgrepxargs, 我得到的以下错误:

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

任何建议一个更强大的使用情况的xargs?

这是上 Mac OS X10.5.3 (豹)与BSD xargs.

有帮助吗?

解决方案

您可以将所有这些组合到一个 find 命令中:

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

这将处理其中包含空格的文件名和目录。您可以使用 -name 来获取区分大小写的结果。

注意:传递给 cp - 标志阻止它处理以 - 开头的文件作为选项。

其他提示

找到。 -print0 | grep --null'FooBar'| xargs -0 ...

我不知道 grep 是否支持 - null ,也不知道 xargs 是否支持 -0 在Leopard上,但在GNU上,这一切都很好。

原始海报想要的最简单的方法是将分隔符从任何空格更改为这样的行尾字符:

find whatever ... | xargs -d "\n" cp -t /var/tmp

这更有效,因为它没有运行“cp”。多次:

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar

我遇到了同样的问题。这就是我解决它的方法:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

我使用 sed 用相同的行替换每行输入,但用双引号括起来。在 sed 手册页中,“ ... ...替换中出现的&符号(`&'')被替换为与RE匹配的字符串... " - 在这种情况下,。* ,整行。

这解决了 xargs:unterminated quote 错误。

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

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

我还测试了您发布的确切语法。这也适用于10.7.5。

只是不用 xargs.它是一个整洁的程序,但是它不好用 find 当遇到不平凡的情况。

这里是一个便携式(POSIX)解决方案,即一个不需要 find, xargscp GNU具体扩展:

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

注意到结束 + 而不是更常见的 ;.

这个解决方案:

  • 正确地处理文件和目录的用嵌入式的空间,新行或任何外来的人物。

  • 适用于任何Unix和Linux的系统,即使是那些不提供GNU工具包。

  • 不使用 xargs 这是一个很好的和有用的程序,但是需要太多的调整和非标准的功能,以正确处理 find 输出。

  • 也是 更有效率 (阅读 速度更快)于接受与多数,如果不是所有其他的答案。

还注意到,尽管什么是说在其他一些答复或评论的引用 {} 是没用(除非你使用异国情调 fishshell)。

在find中使用带有-print0选项的xargs的--null命令行选项。

对于那些依赖命令的人,除了查找,例如 ls

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
find | perl -lne 'print quotemeta' | xargs ls -d

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

我发现以下语法对我有用。

find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200

在这个例子中,我正在寻找安装在“/ usr / pcapps”的文件系统中超过1,000,000字节的最大200个文件。

“发现”之间的Perl线衬。和“xargs”转义/引用每个空白,因此“xargs”将任何带有嵌入空格的文件名传递给“ls”作为一个论点。

请注意,其他答案中讨论的大多数选项在不使用GNU实用程序(例如Solaris,AIX,HP-UX)的平台上不是标准选项。有关“标准”xargs行为,请参阅 POSIX 规范。

我还发现xargs的行为,即使没有输入,它至少运行一次命令,这是一种麻烦。

我编写了自己的私有版本的xargs(xargl)来处理名称中的空格问题(只有换行符分开 - 尽管'find ... -print0'和'xargs -0'组合非常简洁,因为文件名不能包含ASCII NUL'\ 0'字符。我的xargl不像它需要值得发布那样完整 - 特别是因为GNU的设施至少同样好。

使用Bash(不是POSIX),您可以使用进程替换来获取变量中的当前行。这使您可以使用引号来转义特殊字符:

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)

对我而言,我试图做一些与众不同的事情。我想将我的.txt文件复制到我的tmp文件夹中。 .txt文件名包含空格和撇号字符。这适用于我的Mac。

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/

如果您系统上的find和xarg版本不支持 -print0 -0 开关(例如AIX find和xargs),您可以使用这个非常具有代码性的代码:

 find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest

这里sed将负责转义xargs的空格和引号。

在AIX 5.3上测试

我创建了一个名为“xargsL”的小型便携包装脚本。围绕“xargs”它解决了大部分问题。

与xargs相反,xargsL每行接受一个路径名。路径名可以包含除(显然)换行符或NUL字节之外的任何字符。

文件列表中不允许或支持引用 - 您的文件名可能包含各种空格,反斜杠,反引号,shell通配符等 - xargsL会将它们作为文字字符处理,不会造成任何损害。

作为额外的奖励功能,如果没有输入,xargsL将运行一次命令!

注意区别:

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

给予xargsL的任何参数都将传递给xargs。

这是“xargsL”。 POSIX shell脚本:

#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.

set -e
trap 'test $? = 0 || echo "<*> failed!" >& 2' 0

if IFS= read -r first
then
        {
                printf '%s\n' "$first"
                cat
        } | sed 's/./\\&/g' | xargs ${1+"$@"}
fi

将脚本放入$ PATH的某个目录中,不要忘记

$ chmod + x xargsL

那里的脚本使其可执行。

bill_starr的Perl版本不适用于嵌入式换行符(仅处理空格)。对于那些例如没有GNU工具的Solaris,更完整的版本可能是(使用sed)......

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

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

我用 Bill Star的答案在Solaris上略有修改:

find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file

这将在每一行周围加上引号。我没有使用'-l'选项,虽然它可能会有所帮助。

我去的文件列表可能有' - ',但不是新行。我没有将输出文件与任何其他命令一起使用,因为我想在我开始通过xargs大量删除之前查看发现的内容。

我玩了一点,开始考虑修改xargs,并意识到对于我们在这里讨论的那种用例,Python中的简单重新实现是一个更好的主意。

首先,拥有大约80行代码意味着很容易弄清楚发生了什么,如果需要不同的行为,你可以在更短的时间内将其破解成新的脚本需要在Stack&nbsp; 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重写。

您可能需要grep Foobar目录,如:

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .

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

评论意见 user80168 描述一种方法来这样做直接与cp,而不调用cp每个文件:

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

这一工作,因为:

  • cp -t 标志可以得到目标目录的开头附近 cp, 而不是接近结束。从 man cp:
   -t, --target-directory=DIRECTORY
         copy all SOURCE arguments into DIRECTORY
  • -- 标记告诉 cp 解释一切之后作为一个文件,不是一个标志,此文件的开始 --- 不要混淆 cp;你还需要这个因为 -/-- 人物都是通过解释 cp, 而任何其他特殊字符的解释。

  • find -exec command {} + 变种本质上并不相同xargs.从 man find:

   -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.

通过使用这在找到直接的,这样可避免需要一个水管或者一个壳调用,这样,你不需要担心任何讨厌的人物在文件名。

如果您使用的是庆典,你可以转换 stdout 一阵线的 mapfile:

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

好处是:

  • 它是建立在,所以它的速度更快。
  • 执行命令的所有文件的名字在一段时间,所以它的速度更快。
  • 你可以附加其他参数的文件的名称。对于 cp, 你还可以:

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

    然而,某些命令没有这样的特征。

在缺点:

  • 也许不比例以及如果有太多的文件名称。(限制?我不知道,但我已经测试了10MB列文件,其中包括10000+文件的名字没有问题,在Debian)

嗯...谁知道如果Bash提供OS X?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top