我有这个输出。
[root@linux ~]# cat /tmp/file.txt
virt-top time 11:25:14 Host foo.example.com x86_64 32/32CPU 1200MHz 65501MB
ID S RDRQ WRRQ RXBY TXBY %CPU %MEM TIME NAME
1 R 0 0 0 0 0.0 0.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.0 0.0 95:44:07 instance-00000372
virt-top time 11:25:17 Host foo.example.com x86_64 32/32CPU 1200MHz 65501MB
ID S RDRQ WRRQ RXBY TXBY %CPU %MEM TIME NAME
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372
您可以看到它有两个块,我想提取最后一个块(如果您看到第一个块,它的 CPU 全部为零,我不关心),简而言之,我想提取以下最后几行(注意:有时我有两个以上的实例-*)否则我可以使用“tail -n 2”
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372
我已经尝试过 sed/awk/grep 和所有可能的方法,但没有接近期望的结果。
答案1
这感觉有点傻,但是:
$ tac file.txt |sed -e '/^virt-top/q' |tac
virt-top time 11:25:17 Host foo.example.com x86_64 32/32CPU 1200MHz 65501MB
ID S RDRQ WRRQ RXBY TXBY %CPU %MEM TIME NAME
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372
GNUtac
反转文件(许多非 GNU 系统都有tail -r
),sed
选择行,直到第一个以virt-top
.您可以添加sed 1,2d
或tail -n +3
删除标题。
或者在 awk 中:
$ awk '/^virt-top/ { a = "" } { a = a $0 ORS } END {printf "%s", a}' file.txt
virt-top time 11:25:17 Host foo.example.com x86_64 32/32CPU 1200MHz 65501MB
ID S RDRQ WRRQ RXBY TXBY %CPU %MEM TIME NAME
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372
它只是将所有行收集到一个变量中,并清除以 开头的行上的该变量virt-top
。
如果文件非常大,tac
+sed
解决方案必然会更快,因为它只需要读取文件的尾部,而awk
解决方案从顶部读取整个文件。
答案2
您ed
可以使用正则表达式搜索向上使用?pattern?
代替通常的方式/pattern/
(从当前位置上方搜索)。例如:
$ printf '%s\n' '?ID?+1,$p' q | ed -s file.txt
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372
答案3
如果您的输入具有固定数量的块,您也可以执行以下操作:
awk '/^virt-top/ && ++n == 2, 0' <your-file
输出从第二次出现到virt-top
文件末尾的行(0 表示错误的, 意味着结束第一的,最后的范围从未找到)。
答案4
这是另一种处理方法:
$ sed -e '
/\n/q
/virt-top/{h;d;}
H;$!d;g
s/\n//;D
' file.txt
结果
1 R 0 0 0 0 0.6 12.0 96:02:53 instance-0000036f
2 R 0 0 0 0 0.2 12.0 95:44:08 instance-00000372