sed 脚本打印最后一次出现的模式和空行之间的行

sed 脚本打印最后一次出现的模式和空行之间的行

我想使用 sed 打印最后一次出现的模式和空行之间的文本。例如,在以下文件中:

$ cat file
pattern
1
2
3


pattern
4
5


pattern
6
7
8
9


10
11


我只想打印

6
7
8
9

我尝试sed '/pattern/{:1;$!{/^$/!{N;b1};h}};${x;p};d' file这里,但它不起作用。

编辑:这几乎有效:

$ sed -n -e '/pattern/ {n; :a; $!{/\n$/!{N;ba};h} }; $ { x;p }' file
6
7
8
9

如何删除末尾的空行?

Edit2:这是一个有效的解决方案:

$sed '/pattern/{:1;$!{/\n$/!{N;b1};h}};${x;s/pattern\n//;s/\n$//;p};d' file
6
7
8
9

Edit3:这更好:

$sed '/pattern/{:1;$!{/\n$/!{N;b1};s///;h}};${x;s/pattern\n//;p};d' file

答案1

使用流编辑器sed,我们检测模式记录,并从那里跳过该行并循环,直到找到下一个空行。直到那时,累积模式空间中的记录,并在循环后用模式空间的内容覆盖保持空间的内容。对于每个模式都会重新发生这种情况...空行块。在文件末尾,检索hold的内容,如果非空则打印它们。

$ sed -ne '
  /pattern/{
    $d;n
    :loop
      s/\n$//;tdone
      $bdone;N
    bloop
    :done
    x
  }
  ${x;/./p;}
' file
6
7
8
9

最多。行编辑器自然适合此类范围之类的问题编辑

ed -s file <<\eof
a

.
?pattern?+1;/^$/-1p
Q
eof

使用 GNU sed 编辑器的另一种方法是使用范围运算符收集保留空间中的块。

sed -e '
  /pattern/,/^$/!ba
  /./!ba
  H;/pattern/{z;x;}
  :a
  $!d;x;s/.//
' file

我们可以在 slurp 模式下使用 GNU sed 编辑器,并使用正则表达式的贪婪性来到达模式的最后一个实例

sed -Ez '
  s/.*pattern\n(([^\n]+\n)+)(\n.*)?/\1/
' file

下面是 perl 中范围运算符的 sed 等效项。数组@A充当街区线的集水区域。

perl -ne 'next unless
  my $e = /pattern/ .. /^$/;
  @A = $e == 1 ? ()
     :   /./   ? (@A, $_)
     :            @A;
  }{print @A;
' file

答案2

awk '/pattern/,/^$/ { arr[NR]=$0; if (/pattern/) line1=NR; if (/^$/) line2=NR}END{ if (line1) for(i=++line1;i<line2;i++) print arr[i]}' file

/pattern/,/^$/将得到模式和空行之间的线。

然后if (/pattern/) line1=NR给出找到模式的最后一行的记录号。if (/^$/) line2=NR给出最后一个空行的行号找到模式后

最后,两个记录之间的 for 循环返回预期的输出。

如果模式和下一个空行之间没有两行,如果模式不在文件中,或者模式之外没有空行,则此操作将失败。 (取自这里

答案3

在我看来,以前的解决方案也可以扩展来解决这个问题:

printf '%s\n' '?pattern?+1' '. +1,/^$/ -1 p' | ed -s file

这将发送两个命令到ed

  • ?pattern?+1-- 从文件末尾向后搜索模式,然后从该匹配项向前移动一行;这会打印该行
  • . +1,/^$/ -1 p-- 从下一行(当前行加 1)到下一个空行 ( ) 之前的行/^$/ -1,打印这些行

pattern如果和下一个空行之间没有两行,如果pattern不在文件中,或者如果 之外没有空行pattern,则此操作将失败。

答案4

这可能是最简单的awk

$ awk -v RS='' -F '\n' '$1 ~ /pattern/ { hold = $0 } END { if (hold != "") print hold }' file | sed 1d
6
7
8
9

awk这与空输入记录分隔符 一起使用,RS它进入awk“段落阅读模式”,这意味着我们将在 中一次获得一整段$0。我还使用-F '\n'这意味着段落中的每一行都将是一个单独的字段,$1段落的第一行也是如此。

该代码测试正则表达式是否pattern与段落第一行匹配。如果是,它将保留变量中的段落hold

最后,如果hold非空,则打印它。

sed用于删除输出的第一行(匹配 的行pattern)。

要使用 shell 变量$pattern代替静态模式:

pattern=$pattern awk -v RS='' -F '\n' '$1 ~ ENVIRON["pattern"] { hold = $0 } END { if (hold != "") print hold }' file | sed 1d

相关内容