【问题标题】:Display n lines before and m lines after a pattern match were n & m are themselves pattern matches在模式匹配之前显示 n 行和在模式匹配之后显示 m 行 n & m 本身就是模式匹配
【发布时间】:2015-04-02 23:31:56
【问题描述】:

我有这样的数据:

foo
...
bar
...
pattern
...
]

我需要首先匹配“模式”,然后显示“模式”之前的所有内容到“foo”以及模式之后的所有内容到“]”

grep 应该这样做:

grep pattern -A grep foo -B grep ]

可惜没有。

答案不需要包含 grep。 欢迎使用 awk、sed 等。

【问题讨论】:

  • 那么您正在寻找的确切输出是什么?
  • 您的样本输入不足。显示示例输入,其中包括范围重叠和/或文件开始/结束发生在预期范围之前/之后等情况以及相关输出。还要定义“模式”(是 stginr、BRE、ERE 还是其他?)并显示您是否希望整个“单词”或整行仅匹配或部分匹配。否则,我们只是猜测您的几乎所有要求。

标签: awk sed grep


【解决方案1】:

Soo...如果包含与pattern 匹配的内容,您想在匹配foo 的内容和匹配] 的内容之间打印一个部分,对吗?那么

sed -n '/foo/ { :a; N; /\]/!ba /pattern/ p }' filename

sed 代码的工作原理如下:

/foo/ {       # if a line matches foo
  :a          # jump label
  N           # fetch the next line and append it to the pattern space
  /\]/! ba    # if the result does not match ] (that is, if the last fetched
              # line does not contain something that matches ]), go back to :a
  /pattern/ p # if in all these lines, there is something that matches the
              # pattern, print them
}

使匹配在前面不贪心——也就是说,如果在一个文件中

1
foo
2
foo
3
pattern
4
]
5

匹配应包括34 但不包括2,脚本可以这样修改(或类似,取决于您要使用的模式):

sed -n '/foo/ { :a; N; /\n[^\n]*foo/ s/.*\n//; /\]/!ba /pattern/ p }' filename

如果该行中的某些内容与 foo 匹配,/\n[^\n]*foo/ s/.*\n// 将删除最后获取行之前的所有内容。

如果您的图案是线条图案(即,如果它们包含^$),则需要对其进行修改。一旦模式空间中存在多行,^ 将匹配模式空间的开头和$ 的结尾,而不是一行。然后,您可以使用 \n 来匹配行尾。例如,如果您想在精确为 foo] 的行之间进行非贪婪匹配(如果它们之间存在精确为 pattern 的行),您可以使用

sed -n '/^foo$/ { :a; N; /\nfoo$/ s/.*\n//; /\n\]$/!ba /\npattern\n/ p }' filename

【讨论】:

    【解决方案2】:

    这是awk

    awk '/foo/ {t=1} t {a[++b]=$0} /pattern/ {f=1} /^]/ {if (f) for (i=1;i<=b;i++) print a[i];delete a;b=t=f=0}' file
    

    示例数据

    cat file
    foo
    data
    more
    ]
    foo
    ...
    bar
    ...
    pattern
    ...
    ]
    more
    foo
    here
    yes
    ]
    end
    

    awk测试

    awk '/foo/ {t=1} t {a[++b]=$0} /pattern/ {f=1} /^]/ {if (f) for (i=1;i<=b;i++) print a[i];delete a;b=t=f=0}'
    foo
    ...
    bar
    ...
    pattern
    ...
    ]
    

    一些更容易阅读:

    awk '
    /foo/ {t=1} 
    t {a[++b]=$0} 
    /pattern/ {f=1} 
    /^]/ {if (f) 
        for (i=1;i<=b;i++) 
            print a[i]
        delete a
        b=t=f=0
        }
    '
    

    测试是否找到foo,将t设置为true
    如果t 为真,则将所有行存储在数组a
    如果找到pattern,则设置标志f
    如果找到],则测试标志f是否为真,然后打印数组a 重置所有内容并重新开始。

    【讨论】:

      【解决方案3】:

      使用 perl 的单行代码:

      perl -wln -0777 -e 'm/foo((?!foo).)*pattern[^\]]*\]/s and print $&;' [filename]
      

      输入:

      foo
      foo
      ...
      bar
      ...
      pern
      ...
      ]
      ]
      foo
      ... 
      pattern
      ]
      ]
      foo
      ]
      

      输出:

      perl -wln -0777 -e 'm/foo((?!foo).)*pattern[^\]]*\]/s and print $&;' testtest
      foo
      ... 
      pattern
      ]
      

      analysis on regex101

      一些关键点:

      1. 在 perl 中使用m/.../s 开启单行模式参考this post
      2. 正则表达式foo((?!foo).)*pattern[^\]]*\]
        • foo匹配第一个foo
        • ((?!foo).)* 避免在匹配部分使用negative lookahead 匹配foo
        • pattern 匹配模式
        • [^\]]*\] 以下部分不应包含 ] 并以 ] 结尾

      【讨论】:

        猜你喜欢
        • 2013-02-23
        • 1970-01-01
        • 1970-01-01
        • 2013-08-13
        • 1970-01-01
        • 2019-05-05
        • 1970-01-01
        • 2021-06-16
        • 1970-01-01
        相关资源
        最近更新 更多