【问题标题】:How to print a greedy range of lines using awk如何使用 awk 打印一系列贪婪的行
【发布时间】:2015-04-30 21:19:33
【问题描述】:

我遇到了以下问题,但没有找到解决方案,也没有找到为什么 awk 会出现这种奇怪的行为。

假设我在文件中有以下文本:

startcue
This shouldn't be found.

startcue
This is the text I want to find.
endcue

startcue
This shouldn't be found either.

我想找到“startcue”、“This is the text I want to find.”和“endcue”这几行。

我天真地假设通过 awk '/startcue/,/endcue/' 进行简单的范围搜索就可以了,但这会打印出整个文件。我猜 awk 以某种方式找到了第一个范围,但是当第三个 startcue 在打印行时触发时,它会打印所有行,直到文件结尾(不过,这对我来说似乎有点奇怪)。

现在问题是:我怎样才能让 awk 打印出我不想要的行?也许作为一个额外的问题:任何人都可以解释 awk 的行为吗?

谢谢

【问题讨论】:

  • 该范围匹配尽可能多的次数。第一个匹配是第 1 行到endcue,第二个匹配是最后一个startcue 到末尾。所以不应该打印第二个空行。您如何期望 awk 知道要使用哪个 startcue(供您建议的用法)。您可以通过手动保留线条来做您想做的事情(并在您击中新的开始线条时删除以前保存的线条)。
  • 永远不要使用范围表达式,始终使用标志,例如/start/{f=1} f; /end/{f=0}。范围表达式使解决琐碎工作的脚本变得非常简单,但是当您发现即使是最微小的复杂性时,也需要完全重写和/或复制条件。

标签: regex bash search awk range


【解决方案1】:
$ awk '/startcue/{f=1; buf=""} f{buf = buf $0 RS} /endcue/{printf "%s",buf; f=0}' file
startcue
This is the text I want to find.
endcue

【讨论】:

  • 当然,它会初始化一个缓冲区并在找到第一个正则表达式时设置一个标志,在设置标志时每行添加到缓冲区,然后打印缓冲区并在找到第一个正则表达式时重置标志最后一个正则表达式。
  • 我将再次链接到您的fantastic answer,因为查看此模式的其他用途可能有助于人们理解它。
【解决方案2】:

这是一个简单的方法。
由于数据以空行分隔,因此我将 RS 设置为空。
这使得awk 可以处理块中的数据。
然后找到所有以startcue开头并以endcue结尾的块

awk -v RS="" '/^startcue/ && /endcue$/' file
startcue
This is the text I want to find.
endcue

如果startcueendcue 始终是起始行和结束行,并且只在块中出现一次,则应该这样做:(PS 测试确实表明,在块。如果同时找到 startclue 和 endcue,这总是打印块)

awk -v RS="" '/startcue/ && /endcue/' file
startcue
This is the text I want to find.
endcue

这也应该有效:

awk -v RS="" '/startcue.*endcue/' file
startcue
This is the text I want to find.
endcue

【讨论】:

    【解决方案3】:

    为了总结问题,您希望打印从 startcue 到 endcue 的行但如果 endcue 丢失则不需要。 Ed Morton 的方法很好。这是另一种方法:

    $ tac file | awk '/endcue/,/startcue/' | tac
    startcue
    This is the text I want to find.
    endcue
    

    工作原理

    • tac file

      这会以相反的顺序打印行。 taccat 一样,只是行的顺序相反。

    • awk '/endcue/,/startcue/'

      这会打印从 endcue 开始到 startcue 结束的所有行。以这种方式完成时,不会打印缺少结尾线索的段落。

    • tac

      这将再次反转行,以便以正确的顺序返回。

    awk 范围的工作原理

    考虑:

     awk '/startcue/,/endcue/' file
    

    这告诉awk 在找到startcue 时开始打印,并继续打印直到找到endcue。这正是它对您的文件所做的。

    没有暗示规则/startcue/,/endcue/ 本身不能包含startcue 的多个实例。 awk 在看到第一次出现 startcue 时开始打印,并一直持续到如果找到 endcue

    【讨论】:

    • 这只是解决问题。这将打印具有endcue 但没有startcue 的部分。这也远低于 Ed 使用的更直接(没有双关语)的方法。
    • 谢谢!这也很有效,但由于 Ed 提供了一个仅限 awk 的解决方案,我会选择他的。不过很好的解释!
    • @EtanReisner (1) OP 只显示了缺失的结尾,所以,是的,如本答案第一句所述,这个答案只是关于缺失的结尾。 (2) 有时计算机的“效率”很重要。很多时候,更重要的是有效利用程序员的时间。由于这段代码很短,不需要去摸索定义和更新变量的代码,我相信它符合后面效率的含义。
    • 问题张贴者经常省略他们问题的关键细节。为具体细节定制解决方案(当通用解决方案可用时)通常不是回答问题的最佳方式。话虽如此,我并没有像指出 OP(以及后来查看此答案的人)可能不会立即意识到此处存在的细节那样攻击您的答案。
    猜你喜欢
    • 2014-05-20
    • 2021-07-02
    • 2012-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多