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

fed*_*qui 40 regex shell perl awk sed

我有类似下面的文件,我想打印给定的两个模式之间的界限PAT1PAT2.

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

在PAT1和PAT2之间打印行

$ 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文件末尾到最后一个匹配的行.

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

$ 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.

在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
Run Code Online (Sandbox Code Playgroud)

通过放置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
Run Code Online (Sandbox Code Playgroud)

通过放置flag在最开始,它触发先前设置的动作,因此打印结束模式但不打印起始模式.

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

这是基于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解决方案怎么样?

在PAT1和PAT2之间打印行

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)

以上不包括范围边界.

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

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

sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' FILE
Run Code Online (Sandbox Code Playgroud)

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

以下仅包括范围开始:

sed -n '/PAT1/,/PAT2/{//!p}' FILE
Run Code Online (Sandbox Code Playgroud)

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

以下仅包括范围结束:

sed -n '/PAT1/,/PAT2/{//!p;}' FILE
Run Code Online (Sandbox Code Playgroud)

  • 不确定其他版本,但使用GNU sed,第一个可以简化为`sed -n'/ PAT1 /,/ PAT2/{//!p}'file` ...来自[manual](https:/ /www.gnu.org/software/sed/manual/sed.html#Regexp-Addresses)`空正则表达式'//'重复最后一个正则表达式匹配 (3认同)
  • 嘿嘿,经典更短! (2认同)
  • 好像.很难找到一个不兼容的版本来证明这一点.:) (2认同)
  • @AlexHarvey我认为通过分享您的知识来改善其他答案,这是您在这里所做的善意的一个很好的例子。最终,这是我发布此问题时的目标,因此我们可以使用一组规范的(但又一个:P](https://xkcd.com/927/))资源。非常感谢! (2认同)

小智 7

或者:

sed '/START/,/END/!d;//d'
Run Code Online (Sandbox Code Playgroud)

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


kar*_*kfa 6

这是另一种方法

包括两种模式(默认)

$ 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)


Ale*_*vey 6

为了完整起见,这是一个 Perl 解决方案:

PAT1 和 PAT2 之间的打印线 - 包括 PAT1 和 PAT2

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)

在 PAT1 和 PAT2 之间打印线 - 排除 PAT1 和 PAT2

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)

在 PAT1 和 PAT2 之间打印行 - 仅排除 PAT1

perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE
Run Code Online (Sandbox Code Playgroud)

在 PAT1 和 PAT2 之间打印线 - 仅排除 PAT2

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详细讨论了提取一系列行。


Jam*_*own 5

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)


aal*_*ous 5

这就像上面两个最佳答案(awk 和 sed)的脚注。我需要在大量文件上运行它,因此性能很重要。我对 10000 次负载测试给出了 2 个答案:

sedTester.sh

for i in `seq 10000`;do sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' patternTester >> sedTesterOutput; done
Run Code Online (Sandbox Code Playgroud)

awkTester.sh

 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)的两倍。