sed:返回文件末尾之前的最后一个匹配项

9
使用sed,我该如何返回匹配的最后一个结果直到文件末尾? (提示:这里给出了简化版)
到目前为止,我尝试过:
sed -n '/ Statistics |/,$p' logfile.log

该命令从第一个匹配行开始返回所有行(几乎是整个文件)。

我还尝试过:

$linenum=`tail -400 logfile.log | grep -n " Statistics |" | tail -1 | cut -d: -f1`
sed "$linenum,\$!d" logfile.log

这个命令可以正常工作,但不能一次性在ssh连接中运行,真的需要将所有内容放在一个管道中。

日志文件的格式如下:

(每分钟都会在日志文件中写入带有子数据的统计标题,此命令的目的是返回最新的统计标题以及在标题之后发生的任何相关错误)

Statistics |
   Stuff
   More Stuff
   Even more Stuff
Statistics |
   Stuff
   More Stuff
Error: incorrect value
Statistics |
   Stuff
   More Stuff
   Even more Stuff
Statistics |
   Stuff
Error: error type one
Error: error type two

EOF

需要返回的值为:

Statistics |
   Stuff
Error: error type one
Error: error type two
6个回答

18

您的示例脚本在 Statistics 前面有一个空格,但您的示例数据似乎没有。这里的正则表达式假设 Statistics 在行首;如果不正确,请进行调整。

sed -n '/^Statistics |/h;/^Statistics |/!H;$!b;x;p'
当你看到统计信息时,用当前行 (h) 替换保留空间的内容。否则,将内容追加到保留空间 (H)。如果我们没有到文件结尾,在此停止 (b)。在文件结尾时,打印出保留空间 (x 检索保留空间的内容; p 打印)。
在一个 sed 脚本中,命令可以选择性地以 "地址" 为前缀。最常见的是正则表达式,但也可以是行号。地址 /^Statistics |/ 选择与正则表达式匹配的所有行;/^Statistics |/! 选择不匹配正则表达式的行;$! 匹配除文件中最后一行外的所有行。没有显式地址的命令将对所有输入行执行。
编辑一下脚本的详细说明,并添加以下内容。
请注意,如果您需要使用 ssh 将此传递给远程主机,则需要添加额外级别的引用。如果变得太复杂,一种可能的解决方法是将此脚本存储在远程主机上,然后只需运行 ssh remotehost path/to/script。另一个可能的解决方法是更改寻址表达式,使其不包含任何感叹号(这在命令行上,例如 Bash 中会有问题)。
sed -n '/^Statistics |/{h;b};H;${x;p}'

这个方法更加简单!

如果你的ssh管道的标准输入没有被占用,第三种可能的解决方法是从本地主机通过管道将脚本输入。

echo '/^Statistics |/h;/^Statistics |/!H;$!b;x;p' |
ssh remotehost sed -n -f - file

谢谢Tripleee,这个完美地解决了问题! 使用正则表达式4-5年后,我真的不知道h,!H,$!b;x或p标志的含义,但你是个明星! - f2s
1
这不是正则表达式,而是“sed”命令名称。脚本后面的段落是人类可读的翻译。请阅读“sed”手册以获取可用命令列表,或者参考在线教程。很高兴能帮上忙,感谢您的接受! - tripleee

16

如果您有可用的 tac

tac INPUTFILE | sed '/^Statistics |/q' | tac

哇,这甚至更好。我一直在使用上面的正则表达式,但无法通过ssh传递!H和$!b而不出错。 - f2s
@tripleee的sed命令很棒,但这个肯定更快! - Roger Dueck
我使用了一个具有不完整最后一行的日志文件。为了完成该行,我用(cat file;echo)|tac|sed ...替换了第一个tac。这是一个很好的解决方案,因为这些都是你可以记住的东西!谢谢Glenn。 - Rich

3
这可能适合您:
sed '/Statistics/h;//!H;$!d;x' file
Statistics |
   Stuff
Error: error type one
Error: error type two

1
这与我的第二个版本非常相似。使用 d 而不是 b 可以避免使用 -n 选项来抑制打印。然后最后的 p 也是隐式的。 - tripleee

2
如果您满意于使用awk的解决方案,那么这个解决方案基本可行(除了会多出一个空白行):
awk '/^Statistics/ { buf = "" } { buf = buf "\n" $0 } END { print buf }' input.txt

1
这也许可以起作用,是其他人提供的sed解决方案略微简化的版本:
sed -n 'H; /^Statistics |/h; ${g;p;}' logfile.log

输出:

Statistics |
   Stuff
Error: error type one
Error: error type two

1
sed ':a;N;$!ba;s/.*Statistics/Statistics/g' INPUTFILE

应该可以工作(GNU sed 4.2.1)。

它将整个文件读入一个字符串,然后用Statistics替换从开头到最后一个Statistics(包括这个单词),并打印剩下的内容。

希望对你有所帮助。


1
如果输入文件很大,这种方法的可扩展性不太好。 - tripleee
我使用了@ZsoltBotykai提供的解决方案,并进行了一些修改。在我的情况下,我需要删除包含匹配项的最后一行,但我无法做到这一点,所以我用空白替换了匹配项。以下是我使用的代码:sed -i ':a;N;$!ba;s/match//g' 我只需要去掉 .* 即可。 - Andrés Chandía

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接