如何获取从最后一个匹配到文件末尾的行?

Posted

技术标签:

【中文标题】如何获取从最后一个匹配到文件末尾的行?【英文标题】:How to get lines from the last match to the end of file? 【发布时间】:2022-01-08 05:44:59 【问题描述】:

需要在最后一个匹配后打印行到文件末尾。匹配的数量可以是任意的,并且不是确定的。我有一些如下所示的文字。

MARKER
aaa
bbb
ccc
MARKER
ddd
eee
fff
MARKER
ggg
hhh
iii
MARKER
jjj
kkk
lll

想要的输出是

jjj
kkk
lll

我是否将 awk 与 RS 和 FS 一起使用以获得所需的输出?

【问题讨论】:

我在awk 解决方案之上添加了grepsed 解决方案。 所需的输出是否还包含MARKER 或只是您显示的内容? @Allan 没有问题。谢谢。我在 perl 脚本中使用您答案中的第一个命令。 Perl 脚本将 $0 解释为脚本名称,而不是直到 EOF 的行。有没有办法解决这个问题? @Allan 这是我的 perl 脚本中的行 "$data = `zcat file.gz | awk -v RS='MARKER\n' 'ENDprintf $0' | grep 'Data : '`" @Allan 没关系。我必须逃脱$ 0。 printf \$0 【参考方案1】:

您实际上可以使用 awk (gawk) 来完成此操作,而无需使用任何管道。

$ awk -v RS='(^|\n)MARKER\n' 'ENDprintf "%s", $0' file
jjj
kkk
lll

说明:

您通过RS='(^|\n)MARKER\n' 将记录分隔符定义为(^|\n)MARKER\n,默认为EOL 字符 'ENDprintf "%s", $0' => 在文件末尾,打印整行,因为 RS 设置为 (^|\n)MARKER\n$0 将包括所有行,直到 EOF。


另一种选择是使用grep (GNU):
$ grep -zoP '(?<=MARKER\n)(?:(?!MARKER)[^\0])+\Z' file
jjj
kkk
lll

说明:

-z 使用 ASCII NUL 字符作为分隔符 -o 仅打印匹配项 -P 激活 perl 模式 PCRE 正则表达式:(?&lt;=MARKER\n)(?:(?!MARKER)[^\0])+\Z 在这里解释https://regex101.com/r/RpQBUV/2/


最后但同样重要的是,还可以使用以下sed 方法:
sed -n '/^MARKER$/n;h;b;H;$x;p' file
jjj
kkk
lll

说明:

n跳到下一行 h用当前行替换保持空间 H 做同样的事情,但不是替换,而是追加 $x;p 在文件交换结束时 (x) 保留空间和模式空间并打印 (p)

可以变成:

tac file |  sed -n '/^MARKER$/q;p' | tac

如果我们使用tac

【讨论】:

【参考方案2】:

请您尝试关注一下。

tac file | awk '/MARKER/print val;exit val=(val?val ORS:"")$0' | tac

这种方法的好处是awk 将只读取 Input_file 的最后一个块(这实际上是awktac 反向打印之后的第一个块)并在此之后退出。

解释:

tac file |                      ##Printing Input_file in reverse order.
awk '
  /MARKER/                     ##Searching for a string MARKER in a line of Input_file.
    print val                   ##Printing variable val here. Because we need last occurrence of string MARKER,which has become first instance after reversing the Input_file.
    exit                        ##Using exit to exit from awk program itself.
  
  
    val=(val?val ORS:"")$0      ##Creating variable named val whose value will be keep appending to its own value with a new line to get values before string MARKER as per OP question.
  
' |                             ##Sending output of awk command to tac again to make it in its actual form, since tac prints it in reverse order. 
tac                             ##Using tac to make it in correct order(lines were reversed because of previous tac).

【讨论】:

谢谢,成功了!!你能解释一下awk代码吗? @Arteezy,很高兴它对你有所帮助,现在为我的代码添加了完整的解释,干杯。 @Arteezy:我也添加了一个 grep 解决方案!【参考方案3】:

你也可以试试 Perl

$ perl -0777 -ne ' /.*MARKER(.*)/s and print $1 ' input.txt

jjj
kkk
lll

$

【讨论】:

【参考方案4】:

这可能对你有用(GNU sed):

sed -nz 's/.*MARKER.//p' file

这使用贪婪删除所有行,包括最后一次出现的MARKER

【讨论】:

【参考方案5】:

最简单的记忆:

tac fun.log | sed "/MARKER/Q" | tac

【讨论】:

【参考方案6】:

awk 解决方案适用于任何操作系统上的任何awk 版本:

awk '/^MARKER$/ s=""; next  s = s $0 RS END printf "%s", s' file

jjj
kkk
lll

【讨论】:

以上是关于如何获取从最后一个匹配到文件末尾的行?的主要内容,如果未能解决你的问题,请参考以下文章

VBA中如何获取一个表格的行数和列数

如何从最后插入的行中获取值? [复制]

Apache Spark - 如何从两个 RDD 中获取不匹配的行

如何获取提交文件中最后触及的行:PyDriller?

CakePHP:如何获取与 slug 匹配的行的 ID?

如何获取与正则表达式匹配的第一行之后的文件部分