【问题标题】:How to stop sed from buffering?如何停止 sed 缓冲?
【发布时间】:2011-05-14 13:51:51
【问题描述】:

我有一个写入 fd3 的程序,我想用 grep 和 sed 处理该数据。到目前为止,代码如下所示:


exec 3> >(grep "good:"|sed -u "s/.*:\(.*\)/I got: \1/")
echo "bad:data1">&3
echo "good:data2">&3

在我做之前什么都不会输出

exec 3>&-

然后,我想要的一切终于如我所愿:

我得到:数据2

如果我只使用 grep 或只使用 sed,它似乎会立即回复,但混合它们似乎会导致某种缓冲。如何从 fd3 获得即时输出?

【问题讨论】:

    标签: linux bash sed grep


    【解决方案1】:

    我想我找到了。出于某种原因,grep 不会自动进行行缓冲。我在grep 中添加了--line-buffered 选项,现在它会立即响应。

    【讨论】:

    • grep 期望被提供一个巨大的文件或一个巨大的管道流。在这些情况下,完全缓冲的效率要高得多。 (很高兴您使用的是 Linux。我正在输入此内容的这台 OSX 计算机没有 grep 或 sed 的行缓冲选项。)
    • 哇。我很抱歉你的 OSX 机器。更遗憾的是 OSX 实际上要花钱。更少的功能,更多的钱,Apple 是如何做到的?
    • OSX 并不是真正的 for 命令行黑客。我很高兴他们保留了 BSD 基础,但是在这台计算机上,我花更多的时间在 CLI 之外的事情上。
    • 真的吗?我喜欢 BSD 许可证的概念,但从我所见的一点点来看,很多 BSD 软件都缺少几个不错的功能。 BSD 中是否有一些不错的功能而不是 GNU,或者您只是说 BSD bash 比 AppleScript(或任何 Mac 曾经拥有的)更好?
    • 我想你误会了。我有一台 Mac……主要是因为我的工作在 Mac 上标准化。它对于它实际优化的东西来说很好,即大型 GUI 应用程序。不过,我的内心深处是个 Unix 小鬼,我很高兴有一个 Unix shell 环境可供我使用,但如果我想做一些严肃的事情,我会使用我的 Linux 机器。那个环境的 BSDness 是偶然的。这正是 Apple 碰巧用于他们的内核的东西。 (Apple 确实非常讨厌 GPL,唉。我确实认为 GNU shell 环境更胜一筹。)
    【解决方案2】:

    阻止sed 缓冲的另一种方法是通过the s2p sed-to-Perl translator 运行它并插入一个指令以对其进行命令缓冲,可能类似于

    BEGIN { $| = 1 }
    

    这样做的另一个原因是它为您提供了来自 ERE 的更方便的表示法,而不是令人讨厌的反斜杠传统 BRE。您还可以获得完整的 Unicode 属性,这通常很关键。

    但是你不需要翻译器来处理这么简单的sed 命令。你也不需要grepsed。这些都有效:

    perl -nle 'BEGIN{$|=1} if (/good:/) { s/.*:(.*)/I got: $1/; print }'
    
    perl -nle 'BEGIN{$|=1} next unless /good:/; s/.*:(.*)/I got: $1/; print'
    
    perl -nle 'BEGIN{$|=1} next unless /good:/; s/.*:/I got: /; print'
    

    现在您还可以访问最小量词*?+???{N,}?{N,M}?。这些现在允许.*?\S+?[\p{Pd}.]?? 之类的东西,这可能更可取。

    【讨论】:

    • 我实际上按照您的建议走了 perl 路线。我不知道它在 bash 脚本中的用处。 perl 和 bash 绝对可以很好地结合在一起。
    【解决方案3】:

    你只需要告诉 grep 和 sed 不要缓冲行:

    grep --line-buffered 
    

    sed -u
    

    【讨论】:

      【解决方案4】:

      您可以像这样将grep 合并到sed 中:

      exec 3> >(sed -une '/^good:/s//I got: /p')
      echo "bad:data1">&3
      echo "good:data2">&3
      

      解压一下:您可以在任何 sed 命令之前放置一个正则表达式(像往常一样在斜杠之间),这使得它仅适用于与该正则表达式匹配的行。如果s 命令的第一个正则表达式参数是空字符串(s//whatever/),那么它将重用最后一个匹配的正则表达式,在这种情况下是前缀,这样就不必重复自己了。最后,-n 选项告诉 sed 只打印它被明确告知要打印的内容,s 命令上的/p 后缀告诉它打印替换的结果。

      -e 选项不是绝对必要的,但风格很好,它只是表示“下一个参数是 sed 脚本,而不是文件名”。

      总是将 sed 脚本放在单引号中,除非你需要在其中替换一个 shell 变量,即使这样我也会把所有 shell 变量放在单引号中(当然,shell 变量是,双引号)。这样可以避免一堆与反斜杠相关的悲伤。

      【讨论】:

      • 很棒的信息——我曾经知道这一切!
      • sed、awk、grep、perl..它们都那么强大。我希望这只是一个实用程序,因为我没有足够的时间学习所有这些。
      • 正确的思考方式是,/bin/sh + "coreutils" 中的所有内容都是一个实用程序,恰好被分成许多可执行文件。不过,awk 和 perl 是它们自己的东西,老实说,现在我认为学习 awk 不值得费心,因为 perl 确实更好,尤其是如果你学习了它的命令行选项。 (您曾经不得不担心没有 perl 的系统,但我认为这不再重要了。)此外,该领域的一项关键技能是知道何时放弃 shell 并使用 perl 或 python 来完成整个事情而是。
      • 是的,我开始注意到 awk 的失败。我真的很喜欢它的 C-ish 语法,但是当你需要它们时,perl 肯定有额外的功能。我在 python 上还是马马虎虎。 perl 中是否缺少有用的附加功能?
      • Perl 与 Python 几乎完全是个人喜好问题,IMO;两者都具有几乎相同的功能,以及大量的库。就我个人而言,我发现 Python 更适合我想思考需要非平凡数据结构的问题的方式,现在我也使用它来进行文本抨击(超出 sed 可以合理地做的事情),只是因为我更喜欢练习它。但我知道很多人的感觉正好相反。
      【解决方案5】:

      在 Mac 上,brew install coreutils 并使用 gstdbuf 来控制 grep 和 sed 的缓冲。

      【讨论】:

      • JFTR,在 GNU/Linux 系统上只是 stdbuf
      【解决方案6】:

      Turn off buffering in pipe 似乎是最简单和最通用的答案。使用标准缓冲区(coreutils):

      exec 3> >(stdbuf -oL grep "good:" | sed -u "s/.*:\(.*\)/I got: \1/")
      echo "bad:data1">&3
      echo "good:data2">&3
      I got: data2
      

      缓冲有其他依赖,例如取决于 mawk 或者 gawk 读取这个管道:

      exec 3> >(stdbuf -oL grep "good:" | awk '{ sub(".*:", "I got: "); print }')
      

      在这种情况下,mawk 会保留输入,gawk 不会。

      另见How to fix stdio buffering

      【讨论】:

        猜你喜欢
        • 2023-03-20
        • 2010-10-02
        • 1970-01-01
        • 1970-01-01
        • 2015-10-29
        • 2012-06-30
        • 1970-01-01
        • 2015-05-22
        • 1970-01-01
        相关资源
        最近更新 更多