我想使用 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