fed*_*qui 40 regex shell perl awk sed
我有类似下面的文件,我想打印给定的两个模式之间的界限PAT1和PAT2.
1
2
PAT1
3 - first block
4
PAT2
5
6
PAT1
7 - second block
PAT2
8
9
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
我已经阅读了如何选择两个标记图案之间的线条,这些线条可能会出现awk/sed多次,但我很想看到所有可能的组合,无论是否打印图案.
如何选择两种模式之间的线?
fed*_*qui 66
$ awk '/PAT1/,/PAT2/' file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
或者,使用变量:
awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?
/PAT1/匹配具有此文本的行,以及具有此文本的行/PAT2/. /PAT1/{flag=1}设置在一行中找到flag文本的PAT1时间./PAT2/{flag=0}取消在行中找到flag文本的PAT2时间.flag是具有默认操作的模式,即print $0:如果flag等于1,则打印该行.这样,它将打印从发生的时间PAT1到下一个发生的所有那些行PAT2.这也将打印从PAT1文件末尾到最后一个匹配的行.$ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
3 - first block
4
7 - second block
10 - third block
Run Code Online (Sandbox Code Playgroud)
这用于next跳过包含的行PAT1以避免打印.
next可以通过重新组合块来删除此调用:awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file.
$ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
通过放置flag在最后,它触发在PAT1或PAT2上设置的动作:在PAT1上打印,而不是在PAT2上打印.
$ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
Run Code Online (Sandbox Code Playgroud)
通过放置flag在最开始,它触发先前设置的动作,因此打印结束模式但不打印起始模式.
这是基于Ed Morton的解决方案.
awk 'flag{
if (/PAT2/)
{printf "%s", buf; flag=0; buf=""}
else
buf = buf $0 ORS
}
/PAT1/ {flag=1}' file
Run Code Online (Sandbox Code Playgroud)
作为单线:
$ 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
Run Code Online (Sandbox Code Playgroud)
这会将所有选定的行保留在从找到PAT1的那一刻起填充的缓冲区中.然后,它继续填充以下行,直到找到PAT2.在这一点上,它打印存储的内容并清空缓冲区.
hek*_*mgl 31
经典sed解决方案怎么样?
sed -n '/PAT1/,/PAT2/p' FILE
Run Code Online (Sandbox Code Playgroud)
甚至(谢谢Sundeep):
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
Run Code Online (Sandbox Code Playgroud)
以上不包括范围边界.
以下将包括范围边界,这甚至更简单:
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' FILE
Run Code Online (Sandbox Code Playgroud)
以下仅包括范围开始:
sed -n '/PAT1/,/PAT2/{//!p}' FILE
Run Code Online (Sandbox Code Playgroud)
以下仅包括范围结束:
sed -n '/PAT1/,/PAT2/{//!p;}' FILE
Run Code Online (Sandbox Code Playgroud)
小智 7
或者:
sed '/START/,/END/!d;//d'
Run Code Online (Sandbox Code Playgroud)
这将删除除 START 和 END 之间(包括 START 和 END)之外的所有行,然后删除//dSTART 和 END 行,因为//导致 sed 使用以前的模式。
这是另一种方法
包括两种模式(默认)
$ awk '/PAT1/,/PAT2/' file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
屏蔽两种模式
$ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file
3 - first block
4
7 - second block
10 - third block
Run Code Online (Sandbox Code Playgroud)
掩码开始模式
$ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
Run Code Online (Sandbox Code Playgroud)
掩码结束图案
$ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这是一个 Perl 解决方案:
perl -ne '/PAT1/../PAT2/ and print' FILE
Run Code Online (Sandbox Code Playgroud)
或者:
perl -ne 'print if /PAT1/../PAT2/' FILE
Run Code Online (Sandbox Code Playgroud)
perl -ne '/PAT1/../PAT2/ and !/PAT1/ and !/PAT2/ and print' FILE
Run Code Online (Sandbox Code Playgroud)
或者:
perl -ne 'if (/PAT1/../PAT2/) {print unless /PAT1/ or /PAT2/}' FILE
Run Code Online (Sandbox Code Playgroud)
perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE
Run Code Online (Sandbox Code Playgroud)
perl -ne '/PAT1/../PAT2/ and !/PAT2/ and print' FILE
Run Code Online (Sandbox Code Playgroud)
也可以看看:
perldoc perlop有关/PAT1/../PAT2/语法的更多信息,请参阅范围运算符部分:范围运算符
...在标量上下文中,“..”返回一个布尔值。该运算符是双稳态的,就像触发器一样,模拟 sed、awk 和各种编辑器的行范围(逗号)运算符。
有关-n选项,请参阅perldoc perlrun,它使 Perl 的行为类似于sed -n。
Perl Cookbook, 6.8详细讨论了提取一系列行。
grep与PCRE一起使用(如果可用)以打印标记和标记之间的线:
$ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
-Pperl-regexp,PCRE。并非所有grep变体-z 将输入视为一组行,每行以零字节而不是换行符结尾 -o 仅打印匹配(?s)点全部,即。点也找到换行符(.*?) 非贪婪的发现\Z 仅在字符串末尾或末尾换行符之前匹配在标记之间打印行,但结束标记除外:
$ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
Run Code Online (Sandbox Code Playgroud)
(.*?)(?=(\nPAT2|\Z))非贪婪查找,\nPAT2并为\Z在除标记之外的标记之间打印行:
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
3 - first block
4
7 - second block
10 - third block
Run Code Online (Sandbox Code Playgroud)
(?<=PAT1\n) 积极寻找 PAT1\n在标记之间打印行,但开始标记除外:
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
Run Code Online (Sandbox Code Playgroud)
这就像上面两个最佳答案(awk 和 sed)的脚注。我需要在大量文件上运行它,因此性能很重要。我对 10000 次负载测试给出了 2 个答案:
for i in `seq 10000`;do sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' patternTester >> sedTesterOutput; done
Run Code Online (Sandbox Code Playgroud)
for i in `seq 10000`;do awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' patternTester >> awkTesterOutput; done
Run Code Online (Sandbox Code Playgroud)
结果如下:
zsh sedTester.sh 11.89s user 39.63s system 81% cpu 1:02.96 total
zsh awkTester.sh 38.73s user 60.64s system 79% cpu 2:04.83 total
Run Code Online (Sandbox Code Playgroud)
sed 解决方案似乎是 awk 解决方案(Mac OS)的两倍。