如何select两种模式之间的线?

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

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

我已经阅读了如何selectawk / sed中可能出现多次的两个标记模式之间的行,但我很好奇看到所有可能的组合,无论是否打印模式。

我怎样才能select两种模式之间的线?

在PAT1和PAT2之间打印行

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

或者,使用variables:

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

这个怎么用?

  • /PAT1/匹配具有此文本的行,以及/PAT2/
  • /PAT1/{flag=1}设置文本PAT1在一行中的flag
  • /PAT2/{flag=0}当在PAT2中findPAT2文本时将该flag PAT2
  • 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 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的行到文件的结尾

这是基于Ed Morton的解决scheme 。

 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的时刻开始填充的缓冲区中。 然后,它一直填充下面的行,直到findPAT2。 在这一点上,它打印存储的内容并清空缓冲区。

那么经典的sed解决scheme呢?

在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 

在PCRE中使用grep (如果可用)在标记之间打印标记和线条

 $ 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将input视为一组行,每个行以零字节而不是换行符结尾
  • -o只打印匹配
  • (?s) DotAll,即。 点也发现换行符
  • (.*?) nongreedyfind
  • \Z仅匹配string的末尾,或匹配换行符的末尾

在不包括结束标记的标记之间打印行

 $ 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向前查找

在不包括标记的标记之间打印行

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

在不包括开始标记的标记之间打印行

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

这是另一种方法

包括两种模式(默认)

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

掩盖两种模式

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

面具开始模式

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

面膜结束模式

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

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

 $ 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/ – findPAT1PAT2之间的范围并禁止打印;

  • /PAT1/{n}; – 如果它匹配PAT1移动到n (下一行);

  • /PAT2/{d}; – 如果匹配PAT2删除线;

  • p – 打印落在/PAT1/,/PAT2/所有行,并且不会被跳过或删除。

或者:

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

这将删除除START和END之间(包括START和END之间的所有行)之外的所有行,然后//d//导致sed使用先前的模式之后删除START和END行。