如何select两种模式之间的线?
我有一个像下面的文件,我想打印两个给定的模式PAT1
和PAT2
之间的线。
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/
– findPAT1
和PAT2
之间的范围并禁止打印; -
/PAT1/{n};
– 如果它匹配PAT1
移动到n
(下一行); -
/PAT2/{d};
– 如果匹配PAT2
删除线; -
p
– 打印落在/PAT1/,/PAT2/
所有行,并且不会被跳过或删除。
或者:
sed '/START/,/END/!d;//d'
这将删除除START和END之间(包括START和END之间的所有行)之外的所有行,然后//d
从//
导致sed使用先前的模式之后删除START和END行。