【问题标题】:Output text between two regular expression patterns over multiple lines在多行的两个正则表达式模式之间输出文本
【发布时间】:2019-07-03 22:37:21
【问题描述】:

如果我将myfile 带到python 可用的环境中,我可以运行以下命令:

cat myfile | python filter.py

filter.py

import sys

results = []
for line in sys.stdin:
    results.append(line.rstrip("\n\r")) 

start_match = "some text"
lines_to_include_before_start_match = 4
end_match = "some other text"
lines_to_include_after_end_match = 4

for line_number, line in enumerate(results):
    if start_match in line:
        for x in xrange(line_number-lines_to_include_before_start_match, line_number):
            print results[x]

        print line

        for x in xrange(line_number+1, len(results)):       
            if end_match in results[x]:
                print results[x]

                for z in xrange(x+1, x+lines_to_include_after_end_match):
                    print results[z]

                break
            else:
                print results[x]

        print ""

但是我想运行它的环境没有 python。将其转换为我知道环境中存在的 perl 是我唯一的选择吗?是否有简单的 sed 或 awk 命令来执行此操作?

我尝试了以下方法,但由于它错过了 +/- 4 行,因此并不能完全满足我的需求:

cat myfile | sed -n '/some text/,/some other text/p'

[编辑:python 脚本说lines_to_include_after_end_match 是4,但实际上它返回3]

【问题讨论】:

    标签: python awk sed grep


    【解决方案1】:

    鉴于行尾是\n,你可以试试这个:

    awk '/some text/{if(l4)printf l4;p=5} /some other text/{e=1} e && p {p--; if (!p) {e=0;l4="";}} !p && !e { l4 = l4 $0 "\n"; sub(/[^\n]*\n(([^\n]*\n){4})/,"\1",l4);} p' file
    

    请注意,如果您想在结束匹配后打印额外的 4 行,则该标记需要为 6。
    我认为您自己的 python 代码只会在结束匹配后打印另外 3 行。

    为了便于修改,多写几行:

    awk '/some text/{if(l4)printf l4;p=5} 
        /some other text/{e=1} 
        e && p {p--; if (!p) {e=0;l4="";}} 
        !p && !e { l4 = l4 $0 "\n"; sub(/[^\n]*\n(([^\n]*\n){4})/,"\1",l4);} 
        p' file
    

    【讨论】:

    • 是的,你是对的。我的 python 脚本应该只打印出 3 行,这就是我想要它做的。我将更新原始帖子以说明这一点。
    • 我尝试了awk 命令,但它似乎在找到“一些文本”之后返回下一个 p-1 行,然后在“一些其他文本”的第一个实例之前返回 3 行。我希望在“一些文本”之前得到 4 行,在“一些文本”实例之后的“一些文本”和“一些其他文本”的第一个实例之间的所有文本,以及“一些其他”之后的接下来 3 行文字”
    • 是的,some textsome other text 有多个实例。
    • awk 版本 20070501
    • @Joel 已更新。请再试一次,看看它是否按预期工作。
    【解决方案2】:

    这可能对你有用(GNU sed):

    sed ':a;$!{N;s/\n/&/4;Ta};/1st text/{:b;n;/2nd text/!bb;:c;N;s/\n/&/4;Tc;b};$d;D' file
    

    打开一个包含 n 行的窗口,如果这些行包含 1st text,则打印它们并继续打印直到 2nd text,然后再读取 m 行并打印它们。否则,如果是文件末尾,则删除缓冲的行,否则删除缓冲区中的第一行并重复。

    如果匹配文本从行首或行尾开始,请使用:

    sed ':a;$!{N;s/\n/&/4;Ta};/^start/M{:b;n;/end$/M!bb;:c;N;s/\n/&/4;Tc;b};$d;D' file
    

    【讨论】:

      【解决方案3】:

      sed,请尝试:

      sed -n "$(($(sed -n '/some text/=' myfile) - 4)),$(($(sed -n '/some other text/=' myfile) + 4))p" myfile
      
      • 命令sed -n '/some text/='返回匹配some text的行号。
      • 然后从上面的数字中减去 4。
      • 下一部分sed -n '/some other text/='工作类似,得到的行号加4。

      请注意,该脚本会扫描输入文件 3 次,可能不适用于执行时间至关重要的案例。

      [编辑]

      如果文件中有多个"some other text",请尝试:

      sed -n "$(($(sed -n '/some text/=' myfile) - 4)),\$p" myfile | sed "/some other text/{N;N;N;q}"
      

      【讨论】:

      • 虽然我认为这种方法非常聪明,但这个“一些其他文本”的问题在文件中出现了很多次,我只想抓取“一些文本”之间的文本(包括前 4 行) 和“一些其他文本”的第一个实例(包括接下来的 3 行)。
      • 嗯.. 我担心这种可能性:)。我已经更新了我的答案。请你试试看?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      • 2022-12-24
      • 1970-01-01
      • 2020-08-02
      相关资源
      最近更新 更多