【问题标题】:Make xargs execute the command once for each line of input让 xargs 对每一行输入执行一次命令
【发布时间】:2010-09-16 23:44:24
【问题描述】:

如何让 xargs 对给定的每一行输入只执行一次命令? 它的默认行为是将行分块并执行一次命令,将多行传递给每个实例。

来自http://en.wikipedia.org/wiki/Xargs

查找 /path -type f -print0 | xargs -0 rm

在此示例中,find 为 xargs 的输入提供一长串文件名。然后 xargs 将此列表拆分为子列表,并为每个子列表调用一次 rm。这比这个功能等效的版本更有效:

find /path -type f -exec rm '{}' \;

我知道 find 有“exec”标志。我只是从另一个资源中引用一个说明性的例子。

【问题讨论】:

  • 在您提供的示例中,find /path -type f -delete 会更有效:)
  • 尽量不要使用 xargs...
  • OP,我知道这个问题已经很老了,但它仍然出现在谷歌上,恕我直言,接受的答案是错误的。请参阅下面的更长答案。
  • 请考虑将您的接受切换为@Tobia 的答案,这样会更好。接受的答案不处理名称中的空格,并且不允许 xargs 命令的多个参数,这是 xargs 的主要功能之一。

标签: xargs


【解决方案1】:

仅当您的输入中没有空格时,以下内容才有效:

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

从手册页:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

【讨论】:

  • 对我来说,它可以输出为xargs -n 1,因为您给出的那个显示“参数列表太长”。
  • 如果省略MAX-LINES,则默认为1,因此xargs -l就足够了。见info xargs
  • @Wernight:“-n1”不会为每个输入行提供 1 次调用。也许您的输入行太长了。演示:echo "foo bar" | xargs -n1 echo。因此,如果你输入像“ls”这样的东西,它就不能很好地处理空格。
  • 这是错误的。 -L 1 不回答原始问题,-n 1 仅在一种可能的解释中回答。请参阅下面的长答案。
  • @Tobia:它回答了最初的问题,该问题非常具体地与输入行有关。这正是-L 1 所做的。对我来说,OP 似乎显然是在试图避免默认的分块行为,并且由于这是被接受的,我认为我是对的。您的答案解决了一个稍微不同的用例,您也希望分块行为。
【解决方案2】:

您可以分别使用 --max-lines 或 --max-args 标志来限制行数或参数(如果每个参数之间有空格)。

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

【讨论】:

    【解决方案3】:

    在您的示例中,将 find 的输出通过管道传输到 xargs 的目的是 find 的 -exec 选项的标准行为是为每个找到的文件执行一次命令。如果您正在使用 find,并且想要它的标准行为,那么答案很简单 - 不要使用 xargs 开头。

    【讨论】:

    • 实际上,我可以从 OP 的编辑中暗示输入数据与find 无关,这就是为什么他们不喜欢@ 987654322@ 选项。
    【解决方案4】:

    如果您想对来自find每一行 行(即结果)运行命令,那么您需要xargs 做什么?

    试试:

    find 路径 -type f -exec 你的命令 {} \;

    其中文字 {} 被文件名替换,\; 需要文字 find 才能知道自定义命令在那里结束。

    编辑:

    (在编辑您的问题后澄清您了解-exec

    来自man xargs

    -L 最大线数
    每个命令行最多使用 max-lines 个非空白输入行。尾随 空白导致输入行在下一个输入行逻辑上继续。 隐含 -x。

    请注意,如果您使用xargs,以空格结尾的文件名会给您带来麻烦:

    $ mkdir /tmp/bax; cd /tmp/bax
    $ touch a\  b c\  c
    $ find . -type f -print | xargs -L1 wc -l
    0 ./c
    0 ./c
    0 total
    0 ./b
    wc: ./a: No such file or directory
    

    因此,如果您不关心-exec 选项,则最好使用-print0-0

    $ find . -type f -print0 | xargs -0L1 wc -l
    0 ./c
    0 ./c
    0 ./b
    0 ./a
    

    【讨论】:

    • 我发现在命令前面加上 echo 'find 很有趣。 -type f -print0 | xargs -0L1 回显 wc -l'。然后,您可以轻松预览 xargs 将生成的命令,例如查看使用“-0L2”时的差异
    【解决方案5】:
    find path -type f | xargs -L1 command 
    

    就是你所需要的。

    【讨论】:

      【解决方案6】:

      以下命令将找到/path 中的所有文件(-type f),然后使用cp 将它们复制到当前文件夹。请注意使用 if -I %cp 命令行中指定占位符,以便可以将参数放在文件名之后。

      find /path -type f -print0 | xargs -0 -I % cp % .

      使用 xargs (GNU findutils) 4.4.0 测试

      【讨论】:

        【解决方案7】:

        在当前或子文件夹的每个 build.xml 上执行 ant 任务 clean-all。

        find . -name 'build.xml' -exec ant -f {} clean-all \;
        

        【讨论】:

        • 不是每个人都安装了ant
        【解决方案8】:

        另一种选择...

        find /path -type f | while read ln; do echo "processing $ln"; done
        

        【讨论】:

          【解决方案9】:

          这两种方式也有效,并且适用于不使用 find 的其他命令!

          xargs -I '{}' rm '{}'
          xargs -i rm '{}'
          

          示例用例:

          find . -name "*.pyc" | xargs -i rm '{}'
          

          将删除该目录下的所有pyc文件,即使pyc文件包含空格。

          【讨论】:

          • 这会为每个非最佳元素发出一个实用程序调用。
          【解决方案10】:

          在我看来,此页面上的所有现有答案都是错误的,包括标记为正确的答案。这是因为问题措辞含糊。

          总结: 如果您想执行命令“每行输入只执行一次”, 将整行(不带换行符)作为 单参数, 那么这是最好的 UNIX 兼容方式:

          ... | tr '\n' '\0' | xargs -0 -n1 ...
          

          如果您使用 GNU xargs 并且不需要与所有其他 UNIX(FreeBSD、Mac OS X 等)兼容,那么您可以使用 GNU 特定选项 -d

          ... | xargs -d\\n -n1 ...
          

          现在是长篇大论……


          在使用 xargs 时需要考虑两个问题:

          1. 它如何将输入拆分为“参数”;和
          2. 一次传递子命令的参数数量。

          要测试 xargs 的行为,我们需要一个实用程序来显示它被执行了多少次以及有多少个参数。我不知道是否有一个标准的实用程序可以做到这一点,但我们可以很容易地在 bash 中编写代码:

          #!/bin/bash
          echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
          

          假设您在当前目录中将其保存为show 并使其可执行,它的工作原理如下:

          $ ./show one two 'three and four'
          -> "one" "two" "three and four" 
          

          现在,如果最初的问题真的是关于上面的第 2 点(我认为是这样,在阅读了几次之后)并且应该像这样阅读(更改为粗体):

          我怎样才能让 xargs 对每个 argument 的输入只执行一次命令?它的默认行为是将输入分块尽可能少地执行命令,将多个参数传递给每个实例。 em>

          那么答案是-n 1

          让我们比较一下 xargs 的默认行为,它将输入拆分为空格并尽可能少地调用命令:

          $ echo one two 'three and four' | xargs ./show 
          -> "one" "two" "three" "and" "four" 
          

          及其与-n 1 的行为:

          $ echo one two 'three and four' | xargs -n 1 ./show 
          -> "one" 
          -> "two" 
          -> "three" 
          -> "and" 
          -> "four" 
          

          另一方面,如果最初的问题是关于第 1 点的输入拆分,并且应该这样阅读(许多来到这里的人似乎认为是这样,或者混淆了这两个问题):

          我怎样才能让 xargs 执行命令 with 恰好 一个参数 给定的每一行输入?它的默认行为是将行围绕空格分块。

          那么答案就更微妙了。

          有人会认为-L 1 可能会有所帮助,但事实证明它不会改变参数解析。它只对每个输入行执行一次命令,参数与该输入行上的参数一样多:

          $ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
          -> "one" 
          -> "two" 
          -> "three" "and" "four" 
          

          不仅如此,如果一行以空格结尾,则附加到下一行:

          $ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
          -> "one" "two" 
          -> "three" "and" "four" 
          

          显然,-L 并不是要改变 xargs 将输入拆分为参数的方式。

          以跨平台方式(不包括 GNU 扩展)这样做的唯一参数是 -0,它将输入拆分为 NUL 字节。

          然后,只需在tr 的帮助下将换行符转换为 NUL:

          $ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
          -> "one " "two" "three and four" 
          

          现在参数解析看起来一切正常,包括尾随空格。

          最后,如果您将此技术与-n 1 结合使用,无论您有什么输入,每个输入行都会执行一个命令,这可能是查看原始问题的另一种方式(可能是最直观的,因为标题):

          $ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
          -> "one " 
          -> "two" 
          -> "three and four" 
          

          如上所述,如果您使用的是 GNU xargs,则可以将 tr 替换为 GNU 特定选项 -d

          $ echo $'one \ntwo\nthree and four' | xargs -d\\n -n1 ./show 
          -> "one " 
          -> "two" 
          -> "three and four" 
          

          【讨论】:

          • 看起来这是更好的答案。但是,我还是不太明白 -L 和 -n 有什么区别……你能解释一下吗?
          • @olala -L 每个输入行执行一次命令(但行尾的空格将其连接到下一行,并且该行仍然根据空格拆分为参数);而-n 对每个输入参数执行一次命令。如果您在输出示例中计算-> 的数量,则这些是脚本./show 执行的次数。
          • GNU xargs 可能有也可能没有有用的扩展,可以让你取消tr 它有一个非常有用的扩展;来自xargs --help - -d, --delimiter=CHARACTER 输入流中的项目由 CHARACTER 分隔,而不是由空格分隔;禁用引号和反斜杠处理以及逻辑 EOF 处理
          • @Tobia:您介意我编辑您的答案以包含xargs -d '\n' 作为适用于“某些”系统的tr '\n' '\0' | xargs -0 的快捷方式吗?
          • @krlmlr 我加了。
          【解决方案11】:

          如何让 xargs 对给定的每一行输入只执行一次命令?

          -L 1 是一个简单的解决方案,但如果任何文件中包含空格,它就不起作用。这是 find 的 -print0 参数的一个关键功能——用 '\0' 字符而不是空格分隔参数。这是一个例子:

          echo "file with space.txt" | xargs -L 1 ls
          ls: file: No such file or directory
          ls: with: No such file or directory
          ls: space.txt: No such file or directory
          

          更好的解决方案是使用tr 将换行符转换为空(\0)字符,然后使用xargs -0 参数。这是一个例子:

          echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
          file with space.txt
          

          如果您需要限制调用次数,您可以使用-n 1 参数为每​​个输入调用一次程序:

          echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
          

          这还允许您在 将中断转换为空值之前过滤 find 的输出。

          find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
          

          【讨论】:

          • 第二个代码块 tr '\n' '\0\ => tr '\n' '\0' 中存在语法错误,我尝试修复此问题,但“编辑必须位于至少 6 个字符”(这似乎和 git 拒绝提交一样愚蠢,因为我的更改少于 6 个字符)
          • 这是什么意思:“使用-L 的另一个问题是它不允许每个xargs 命令调用有多个参数。” ?
          • 我已经改进了我的答案以删除那些无关的信息@Moberg。
          【解决方案12】:

          我似乎没有足够的声望给Tobia's answer above添加评论,所以我添加这个“答案”来帮助我们这些想要在Windows平台上以同样的方式尝试xargs的人。

          这是一个 Windows 批处理文件,它与 Tobia 的快速编码“显示”脚本执行相同的操作:

          @echo off
          REM
          REM  cool trick of using "set" to echo without new line
          REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
          REM
          if "%~1" == "" (
              exit /b
          )
          
          <nul set /p=Args:  "%~1"
          shift
          
          :start
          if not "%~1" == "" (
              <nul set /p=, "%~1"
              shift
              goto start
          )
          echo.
          

          【讨论】:

            【解决方案13】:

            @Draemon 的答案似乎与“-0”正确,即使文件中有空格。

            我正在尝试 xargs 命令,发现“-0”与“-L”完美配合。甚至空格也被处理(如果输入为空终止)。下面是一个例子:

            #touch "file with space"
            #touch "file1"
            #touch "file2"
            

            以下将拆分空值并对列表中的每个参数执行命令:

             #find . -name 'file*' -print0 | xargs -0 -L1
            ./file with space
            ./file1
            ./file2
            

            所以-L1 如果与“-0”一起使用,将在每个以空字符结尾的字符上执行参数。要查看差异,请尝试:

             #find . -name 'file*' -print0 | xargs -0 | xargs -L1
             ./file with space ./file1 ./file2
            

            即使这将执行一次:

             #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
            ./file with space ./file1 ./file2
            

            该命令将执行一次,因为“-L”现在不会在空字节上拆分。您需要同时提供“-0”和“-L”才能工作。

            【讨论】:

              猜你喜欢
              • 2021-11-08
              • 2016-08-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-05-30
              • 1970-01-01
              • 1970-01-01
              • 2017-04-03
              相关资源
              最近更新 更多