web-dev-qa-db-ja.com

正規表現が一致するまで下から行を抽出する

この出力があります。

[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

あなたはそれが2つのブロックを持っているのを見ることができ、私は最後のブロックを抽出したい(あなたが最初のブロックを見るならそれは私が気にしないすべての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とすべての可能な方法を試しましたが、希望する結果に近づきませんでした。

4
Satish

これは少しばかげた感じですが、

$ 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

GNU tacはファイルを逆にします(多くの非GNUシステムには代わりにtail -rがあります)、sedvirt-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ソリューションはより高速になるはずです。上からファイル全体。

8
ilkkachu

edを使用すると、通常の?pattern?(現在の位置の上から検索)の代わりに/pattern/を使用してregex-search pwardを使用できます。だから例えば:

$ 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
4
steeldriver

入力に固定数のブロックがある場合は、次のようなこともできます。

awk '/^virt-top/ && ++n == 2, 0' <your-file

2からラインを出力するにはndvirt-topの出現からファイルの終わりまで(0はfalseを意味し、その終わりを意味しますfirstlastの範囲は決して見つかりました)。

3

文字列virt-topで始まる行から最後のレコードをedを使用して最後まで取得します(数メガバイトではなく、表示されているものと同等のサイズのファイルを処理するのに適しています)。

$ printf '%s\n' '?^virt-top?,$p' | ed -s file
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

または、サブストリングinstanceを含む最後のvirt-top行の直後の行:

$ printf '%s\n' '?^virt-top?,$g/instance/p' | ed -s file
    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

その最後のedコマンド?^virt-top?,$g/instance/pは、最初にvirt-topで始まる最後の行からの範囲を指定します(最後にedが最後のバッファーの行と?re?を逆方向に検索して)バッファーの最後($)に移動し、コマンドg/instance/pをこれらの行に適用します。 g/re/pコマンドは、指定された正規表現に一致する範囲内のすべての行を出力します(ところでgrepはその名前を取得した場所です)。


awkの使用:

$ awk '/^virt-top/ { lines = "" } /instance/ { lines = (lines == "" ? $0 : lines ORS $0) } END { print lines }' file
    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

これにより、部分文字列linesを含む行がある場合は、入力行がinstanceに保存されます。これらの行は最後に印刷されます。 virt-topで始まる行が見つかると、保存された行は破棄されます。

sedとほぼ同じで、ホールドスペースを使用してlinesコードのawk変数と同等の機能を果たします。

$ sed -n '/^virt-top.*/{ s///; x; d; }; /instance/H; ${ x; s/\n//; p; }' file
    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
1
Kusalananda

これを処理する別の方法を次に示します。

$ 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
1
Rakesh Sharma