【问题标题】:How do I include a pipe | in my linux find -exec command?如何包含管道 |在我的 linux 中找到 -exec 命令?
【发布时间】:2010-09-23 08:23:04
【问题描述】:

这不起作用。这可以在find中完成吗?还是我需要 xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;

【问题讨论】:

    标签: linux command-line


    【解决方案1】:

    如果您正在寻找一个简单的替代方案,可以使用循环来完成:

    for i in $(find -name 'file_*' -follow -type f); do
      zcat $i | agrep -dEOE 'grep'
    done
    

    或者,更通用且易于理解的形式:

    for i in $(YOUR_FIND_COMMAND); do
      YOUR_EXEC_COMMAND_AND_PIPES
    done
    

    并将 YOUR_EXEC_COMMAND_AND_PIPES 中的任何 {} 替换为 $i

    【讨论】:

      【解决方案2】:

      我发现运行字符串 shell 命令 (sh -c) 效果最好,例如:

      find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;
      

      【讨论】:

        【解决方案3】:

        您还可以通过管道连接到while 循环,该循环可以对find 定位的文件执行多项操作。所以这里有一个用于在jar 档案中查找给定 java 类文件的文件夹,其中包含 jar 文件的大型发行版

        find /usr/lib/eclipse/plugins -type f -name \*.jar | while read jar; do echo $jar; jar tf $jar | fgrep IObservableList ; done
        

        关键是while 循环包含多个引用传入文件名的命令,这些命令以分号分隔,并且这些命令可以包括管道。因此,在该示例中,我回显了匹配文件的名称,然后列出了归档过滤中给定类名的内容。输出如下:

        /usr/lib/eclipse/plugins/org.eclipse.core.contenttype.source_3.4.1.R35x_v20090826-0451.jar /usr/lib/eclipse/plugins/org.eclipse.core.databinding.observable_1.2.0.M20090902-0800.jar org/eclipse/core/databinding/observable/list/IObservableList.class /usr/lib/eclipse/plugins/org.eclipse.search.source_3.5.1.r351_v20090708-0800.jar /usr/lib/eclipse/plugins/org.eclipse.jdt.apt.core.source_3.3.202.R35x_v20091130-2300.jar /usr/lib/eclipse/plugins/org.eclipse.cvs.source_1.0.400.v201002111343.jar /usr/lib/eclipse/plugins/org.eclipse.help.appserver_3.1.400.v20090429_1800.jar

        在我的 bash shell (xubuntu10.04/xfce) 中,它确实使匹配的类名变为粗体,因为fgrep 突出显示了匹配的字符串;这使得扫描数百个搜索过的jar 文件列表并轻松查看任何匹配项变得非常容易。

        在 Windows 上你可以做同样的事情:

        for /R %j in (*.jar) do @echo %j & @jar tf %j | findstr IObservableList
        

        请注意,在 Windows 上,命令分隔符是 '&' 而不是 ';'并且“@”会抑制命令的回显,以提供整洁的输出,就像上面的 linux find 输出一样;尽管findstr 不会使匹配的字符串变为粗体,但您必须更仔细地查看输出才能看到匹配的类名。事实证明,windows的'for'命令知道很多技巧,比如循环遍历文本文件......

        享受

        【讨论】:

          【解决方案4】:

          将管道符号解释为运行多个进程并将一个进程的输出通过管道传输到另一个进程的输入的指令的工作是 shell(/bin/sh 或等效项)的职责。

          在您的示例中,您可以选择使用顶级 shell 来执行管道,如下所示:

          find -name 'file_*' -follow -type f -exec zcat {} \; | agrep -dEOE 'grep'
          

          就效率而言,此结果需要调用一次 find、多次调用 zcat 和一次 agrep。

          这将导致仅生成一个 agrep 进程,该进程将处理多次调用 zcat 产生的所有输出。

          如果你出于某种原因想要多次调用 agrep,你可以这样做:

          find . -name 'file_*' -follow -type f \
              -printf "zcat %p | agrep -dEOE 'grep'\n" | sh
          

          这会使用管道构造一个命令列表来执行,然后将这些命令发送到一个新的 shell 以实际执行。 (省略最后的“| sh”是调试或执行此类命令行空运行的好方法。)

          就效率而言,此结果需要调用一次 find、一次调用 sh、多次调用 zcat 和多次调用 agrep。

          就命令调用次数而言,最有效的解决方案是 Paul Tomblin 的建议:

          find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'
          

          ...这需要调用一次 find、一次调用 xargs、几次调用 zcat 和一次调用 agrep。

          【讨论】:

          • xargs 的另一个优点是,您可以通过使用 -P 开关 (-P 0) 来加快现代多核 cpu 的速度。
          • 是的,-P 开关确实是一种加快执行速度的好方法。不幸的是,您冒着将并行 zcat 进程的输出通过管道传输到交错的 agrep 的风险,这会影响结果。可以使用以下命令演示此效果: echo -e "1\n2" | xargs -P 0 -n 1 是 |独特的
          • @Adam,我已经按照您的建议进行了更改。
          • 您可以为其安装出色的 xjobs 命令(最初来自 Solaris)
          • 一个更简单、更通用的答案是stackoverflow.com/a/21825690/42973-exec sh -c "… | … " \;
          【解决方案5】:

          解决方法很简单:通过 sh 执行

          ... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
          

          【讨论】:

          • 上述建议可以满足 OP 试图完成的任务,但这是实际回答所提问题的建议。这样做是有原因的 - exec 比仅对 find 返回的文件进行操作要强大得多,尤其是与 test 结合使用时。例如:find geda-gaf/ -type d -exec bash -c 'DIR={}; [[ $(find $DIR -maxdepth 1 |xargs grep -i spice |wc -l) -ge 5 ]] && echo $DIR' \;将返回搜索路径中包含单词 spice 的所有文件中总共包含超过 5 行的所有目录
          • 最佳答案。 grepping 整个输出(正如其他答案所暗示的)与 grep 每个文件不同。提示:除了 sh,你可以使用任何你想要的 shell(我用 bash 试过,它运行正常)。
          • 确保不要忽略-c 选项。否则你会得到一个令人费解的No such file or directory 错误信息。
          • 这里有一个很棒的 ps 替代品,它在 exec'd shell 中使用带有管道的 find:/usr/bin/find /proc -mindepth 1 -maxdepth 1 -type d -regex '.*/ [0-9]+' -print -exec bash -c "cat {}/cmdline | tr '\\0' ' ' ; echo" \;
          • 使用正则表达式 find -type f -name '*.mdds' -exec sh -c "echo {} | sed -e 's/_[0-9]\+//g' | xargs mv {}" \; 查找文件并使用 sed 重命名文件的示例
          【解决方案6】:
          find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'
          

          【讨论】:

          • 出于效率原因,希望避免使用 -print 和 xargs。也许这真的是我的问题:find 无法通过 -exec 处理管道命令
          • 这不适用于名称中带有空格的文件;修复,将 -print 替换为 -print0 并将 -0 选项添加到 xargs
          • @someguy - 什么?出于效率原因避免使用 xargs?调用 zcat 的一个实例,并将多个文件的列表传递给它,比为每个找到的文件执行它的新实例更有效。
          • @Adam - 我已经按照您的建议进行了更改。 99% 的时间在我进行查找时,它都在我的源代码目录中,并且没有任何文件有空格,所以我不打扰 print0。现在我的文档目录,另一方面,我记得 print0。
          猜你喜欢
          • 1970-01-01
          • 2010-09-08
          • 2012-06-18
          • 1970-01-01
          • 2012-03-19
          • 1970-01-01
          • 1970-01-01
          • 2017-04-23
          • 2017-01-22
          相关资源
          最近更新 更多