首页 文章

如何选择两种模式之间的线?

提问于
浏览
31

我有一个像下面这样的文件,我想打印两个给定模式之间的线 PAT1PAT2 .

1
2
PAT1
3    - first block
4
PAT2
5
6
PAT1
7    - second block
PAT2
8
9
PAT1
10    - third block

我已阅读How to select lines between two marker patterns which may occur multiple times with awk/sed但我很想看到所有可能的组合,无论是否打印图案 .

如何选择两种模式之间的线?

6 回答

  • 60

    在PAT1和PAT2之间打印行

    $ awk '/PAT1/,/PAT2/' file
    PAT1
    3    - first block
    4
    PAT2
    PAT1
    7    - second block
    PAT2
    PAT1
    10    - third block
    

    或者,使用变量:

    awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file
    

    这是如何运作的?

    • /PAT1/ 匹配具有此文本的行,以及 /PAT2/ .

    • /PAT1/{flag=1} 在行中找到文本 PAT1 时设置 flag .

    • /PAT2/{flag=0} 在行中找到文本 PAT2 时取消设置 flag .

    • flag 是具有默认操作的模式,即 print $0 :如果 flag 等于1,则打印该行 . 这样,它将打印从 PAT1 发生的所有那些行,直到下一个 PAT2 被看到 . 这也将打印从 PAT1 的最后一个匹配到文件末尾的行 .

    在PAT1和PAT2之间打印行 - 不包括PAT1和PAT2

    $ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
    3    - first block
    4
    7    - second block
    10    - third block
    

    这使用 next 跳过包含 PAT1 的行,以避免打印 .

    可以通过重新组合块来删除对 next 的调用: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file .

    在PAT1和PAT2之间打印行 - 包括PAT1

    $ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
    PAT1
    3    - first block
    4
    PAT1
    7    - second block
    PAT1
    10    - third block
    

    通过在最后放置 flag ,它会触发在PAT1或PAT2上设置的操作:在PAT1上打印,而不是在PAT2上打印 .

    在PAT1和PAT2之间打印行 - 包括PAT2

    $ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
    3    - first block
    4
    PAT2
    7    - second block
    PAT2
    10    - third block
    

    通过在最开始放置 flag ,它会触发先前设置的操作,从而打印关闭模式但不打印开始模式 .

    在PAT1和PAT2之间打印行 - 如果没有其他PAT2发生,则排除从最后一个PAT1到文件末尾的行

    这基于a solution by Ed Morton .

    awk 'flag{
            if (/PAT2/)
               {printf "%s", buf; flag=0; buf=""}
            else
                buf = buf $0 ORS
         }
         /PAT1/ {flag=1}' file
    

    作为单线:

    $ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file
    3    - first block
    4
    7    - second block
    
    # note the lack of third block, since no other PAT2 happens after it
    

    这会将所有选定的行保留在从找到PAT1的那一刻起填充的缓冲区中 . 然后,它继续填充以下行,直到找到PAT2 . 在这一点上,它打印存储的内容并清空缓冲区 .

  • 3

    经典 sed 解决方案怎么样?

    在PAT1和PAT2之间打印行

    sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' file
    

    甚至(谢谢Sundeep):

    sed -n '/PAT1/,/PAT2/{//!p}'
    

    以上不包括范围边界 .

    在PAT1和PAT2之间打印行 - 包括PAT1和PAT2

    以下将包括范围边界,这甚至更简单:

    sed -n '/PAT1/,/PAT2/p' file
    

    在PAT1和PAT2之间打印行 - 包括PAT1

    以下仅包括范围开始:

    sed -n '/PAT1/,/PAT2/{/PAT2/!p}' file
    

    在PAT1和PAT2之间打印行 - 包括PAT2

    以下仅包括范围结束:

    sed -n '/PAT1/,/PAT2/{/PAT1/!p}' file
    
  • 25

    使用 grep 与PCRE(如果可用)到 print markers and lines between markers

    $ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
    PAT1
    3    - first block
    4
    PAT2
    PAT1
    7    - second block
    PAT2
    PAT1
    10    - third block
    
    • -P perl-regexp,PCRE . 并非所有 grep 变体

    • -z 将输入视为一组行,每行以零字节而不是换行符结尾

    • -o 仅打印匹配

    • (?s) DotAll,即 . dot也找到换行符

    • (.*?) 不经常发现

    • \Z 仅在字符串结尾处或在结尾处的换行符之前匹配

    Print lines between markers excluding end marker

    $ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
    PAT1
    3    - first block
    4
    PAT1
    7    - second block
    PAT1
    10    - third block
    
    • (.*?)(?=(\nPAT2|\Z)) nongreedy查找 \nPAT2\Z 的预测

    Print lines between markers excluding markers

    $ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
    3    - first block
    4
    7    - second block
    10    - third block
    
    • (?<=PAT1\n) PAT1\n 正向后视

    Print lines between markers excluding start marker

    $ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
    3    - first block
    4
    PAT2
    7    - second block
    PAT2
    10    - third block
    
  • 1

    这是另一种方法

    Include both patterns (default)

    $ awk '/PAT1/,/PAT2/' file
    PAT1
    3    - first block
    4
    PAT2
    PAT1
    7    - second block
    PAT2
    PAT1
    10    - third block
    

    Mask both patterns

    $ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file
    3    - first block
    4
    7    - second block
    10    - third block
    

    Mask start pattern

    $ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file
    3    - first block
    4
    PAT2
    7    - second block
    PAT2
    10    - third block
    

    Mask end pattern

    $ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file
    PAT1
    3    - first block
    4
    PAT1
    7    - second block
    PAT1
    10    - third block
    
  • 1

    您可以使用 sed 通过使用 -n 抑制模式空间的正常打印来执行您想要的操作 . 例如,要在结果中包含模式,您可以执行以下操作:

    $ sed -n '/PAT1/,/PAT2/p' filename
    PAT1
    3    - first block
    4
    PAT2
    PAT1
    7    - second block
    PAT2
    PAT1
    10    - third block
    

    要排除模式并只打印它们之间的内容:

    $ sed -n '/PAT1/,/PAT2/{/PAT1/{n};/PAT2/{d};p}' filename
    3    - first block
    4
    7    - second block
    10    - third block
    

    这打破了

    • sed -n '/PAT1/,/PAT2/ - 找到 PAT1PAT2 之间的范围并禁止打印;

    • /PAT1/{n}; - 如果匹配 PAT1 移至 n (下一行)行;

    • /PAT2/{d}; - 如果匹配 PAT2 删除行;

    • p - 打印 /PAT1/,/PAT2/ 内所有未被跳过或删除的行 .

  • 4

    或者:

    sed '/START/,/END/!d;//d'
    

    这将删除除START和END之间和之外的所有行,然后 //d 删除START和END行,因为 // 会导致sed使用以前的模式 .

相关问题