【问题标题】:How to get all lines between the one containing a certain string and another one containing another string PLUS the line before如何获取包含某个字符串的行和包含另一个字符串的另一行之间的所有行加上之前的行
【发布时间】:2021-04-14 22:14:48
【问题描述】:

我花了一些时间在网上搜索,但没有找到答案。 假设我有一个包含以下行的文件:

aaaaaaa
vvvvv
ggggg
yyyyyyyyy
ffffff
rrrrrrrr
uuuuu
ssssssssssss
zzzzz
hhhhhhhh

我知道如何使用 awk 查找从包含“ffffff”的行到包含“uuuuu”的所有行:

awk '/ffffff/,/uuuuu/' file

但是我怎样才能得到我找到的第一行之前的行(即“yyyyyyyyy”)?有没有像 grep -B 1 这样的东西来做到这一点? 我想得到的是:

yyyyyyyyy
ffffff
rrrrrrrr
uuuuu

提前致谢。

【问题讨论】:

  • 如果输入中不存在 uuuuu,您得到的一些答案将从 fffff 打印到文件末尾 - 这是正确的行为吗?如果只有 uuuuu 存在而没有前面的 ffffff,有些会打印 - 对吗?另外应该如何处理嵌套和/或重叠的分隔符?
  • 这些都是好问题。我没有想到,假设我的文件是正确的,即总是有第一个字符串和第二个字符串......但你是对的,假设它总是正确的可能不是一个好主意......
  • 在非晴天的情况下,打印您想要的文本总是微不足道的,而不打印您不想要的文本则要困难得多。一些答案的另一个问题是,如果ffffff 出现在输入的第一行,他们将在 ffffff->uuuuu 文本之前打印一个空行 - 这是期望的行为吗?
  • 我很确定 ffffff 永远不会出现在第一行。

标签: awk


【解决方案1】:

仅在输入中同时存在 ffffff 和 uuuuu 时打印,如果 ffffff 是输入中的第一行,则不打印空行:

$ cat tst.awk
/ffffff/ { f = 1 }
f {
    rec = rec $0 ORS
    if ( /uuuuu/ ) {
        printf "%s", rec
        f = 0
    }
    next
}
{ rec = $0 ORS }

$ awk -f tst.awk file
yyyyyyyyy
ffffff
rrrrrrrr
uuuuu

【讨论】:

  • 这在我的情况下没有得到任何结果,但这可能是因为我正在 Cygwin 中测试它……我将在 Linux 中尝试。无论如何,谢谢你的帮助。
  • 不,和cygwin无关。实际上,当我编写和测试它时,我自己正在使用 cygwin。如果它对您不起作用,那么您复制/粘贴不正确,或者您运行它的数据看起来不像您问题中的数据(例如,您可能没有 uuuuu 存在)。如果您运行我在您发布的示例输入上发布的脚本产生您发布的预期输出。
  • 糟糕,你说得对,我打错了。你的答案很完美。
【解决方案2】:

略有不同

$ awk '/fffff/{print p; f=1} f; /uuuuu/{f=0} {p=$0}' file

yyyyyyyyy
ffffff
rrrrrrrr
uuuuu

假设开始/结束标记不重叠但可以存在多次。

说明:首先从fffff 开始,它将打印上一行(在p 中捕获)并设置一个标志。 f; 表示如果设置了标志,则打印当前行,f==1{print $0} 的简写。如果看到uuuuu,则下一条语句重置标志,最后我们捕获要在第一条语句中使用的行;除了使用它的一种情况外,这似乎没有必要,但很可能比每次比较便宜。

请注意,这可能是处理边缘情况(多个开始/结束标记、无结束标记等)的功能有限的最简单脚本。

【讨论】:

  • !f && /ffffff/ 修复了我认为无法匹配的事件
  • 这在我的情况下工作得很好,但是I don't understand it...所以我需要阅读手册才能理解...谢谢您的帮助。
  • @Stantheman 如果uuuuu 不存在,这是将从第一个fffff 打印到文件末尾的答案之一 - 这是所需的行为吗?
  • @Ed Morton 不是。但我不确定在这种情况下最好的行为是什么。复制所有行直到文件末尾不是我想要的,因为我的目标是将有用数据从 500M 日志中获取到 ~250k 文件中。但另一方面,它会生成原始日志文件的部分备份(将被删除)……
  • 您始终可以根据数据是否采用您期望的格式来设置/使用 awk 脚本的退出状态,从而决定是否应该制作原始文件的副本。
【解决方案3】:

你可以使用这个awk:

awk '/ffffff/ {flag=1; prev=p} flag {if (prev!="") print prev; prev=""; print} /uuuuu/ {flag=0} {p=$0}' file

yyyyyyyyy
ffffff
rrrrrrrr
uuuuu

【讨论】:

    【解决方案4】:

    另一个awk使用循环for

    awk -v OFS='\n'  '{for (i=1; i<=NF; i++) if ($i == "ffffff"|| $i == "uuuuu") print p,$0} {p=$0}' file
    yyyyyyyyy
    ffffff
    rrrrrrrr
    uuuuu
    
    • 如果只有一个字段,则不带for
    awk -v OFS='\n'  '{ if ($1 == "ffffff" || $1 == "uuuuu") print p,$0} {p=$0}' file
    yyyyyyyyy
    ffffff
    rrrrrrrr
    uuuuu
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-25
      • 2018-09-19
      • 2021-04-07
      • 2020-01-16
      • 1970-01-01
      • 2021-12-23
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多