ファイルの末尾に2番目に現れる文字列を探す

ファイルの末尾に2番目に現れる文字列を探す

次のログファイルがあります。

Another thousand lines above this
I 10/03/15 12:04AM 42 [Important] 4th to last
I 10/03/15 04:31AM 42 (534642712) [1974,2106,258605080,0,0,32817,30711]
I 10/03/15 04:33AM 42 (2966927) [91,0,2966927,0,0,291,291]
I 10/03/15 04:52AM 42 (3026559) [93,0,3026559,0,0,314,314]
I 10/03/15 04:55AM 42 (3065494) [94,0,3065494,0,0,301,301]
I 10/03/15 05:04AM 42 [Important] 3rd to last
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

ファイル全体で一定に保たれる唯一のパターンは、[Important]各発生間の行数を含む他のすべてが変更されることです。[Important]

ファイルの終わりを取得しようとしています。最後の項目を無視し、最後の項目で2番目の項目を見つけて、残りのファイルを別のファイルに抽出しようとします。

これが私が試したことですが、tacを使用すると、最後から2番目のオカレンスを選択することはできません。私は何をしていますか?

<logfile tac | sed '/Important/q' | tac >  output_file

出力は次のようになります。

I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

答え1

「重要」のあるすべての行を探し、最後の2行を選択し、行番号を取得して範囲を印刷します。

sed -n `grep -n Important log | tail -n 2 | cut -d : -f 1 | tr '\n' ',' | sed -e 's#,$#p#'` log

出力は予想通りです。

I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

スクリプトとして:

#!/bin/bash
lines=`grep -n Important log | tail -n 2 | cut -d : -f 1`
range=`echo "${lines}" | tr '\n' ',' | sed -e 's#,$#p#'`
sed -n "${range}" log

答え2

$ awk '/Important/{pen=s; s=$0;next} s{s=s"\n"$0} END{print pen "\n" s}' logfile
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42  (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence

どのように動作しますか?

awk は、入力ファイルのすべての行を暗黙的に繰り返します。各発生後、Importantこの行を変数に保存しますs。新しい行に達すると、Important重要な行の以前のセットが変数に転送され、penその中に新しい行が保存され始めますs

pen2番目(2番目)の部分がありますImportants最後(最後)のImportant部分があります。最後にpen合計を印刷しますs

もっと詳しく:

  • /Important/{pen=s; s=$0;next}

    行にが含まれている場合は、Important変数の内容をに移動して現在の行を保存sします。その後、残りのコマンドをスキップして次の行に移動します。pens

  • s{s=s"\n"$0}

    ここに来たら現在の行にはImportant

    s値に設定されている場合は、現在の行をその値に追加します。

  • END{print pen "\n" s}

    ファイルの終わりに達した後、pen合計を印刷しますs

答え3

edオプションの場合:

ed -s file <<EOF
1
?Important
?
;w output_file
Q
EOF

答え4

sedがファイル全体をバッファリングできる場合(GNUなどを使用している場合は可能です)
(最後の編集:ここで複数のBrainosを修正しました)

sed -En 'H;$!d
     g;s/.*[\n](.*Important.*\n.*Important[^\n]*).*/\1/p
'    

ファイルの最後まで各行をバッファリングH;$!d(「保留」)します。\n以下は、$!d最後の行がバッファリングされた後にのみ実行されます。g getsバッファ。

正規表現を理解するには、正規表現が一番左にあり、最も長いことを覚えておいてください。リード。*検索最後のゲーム次の内容。Haは無条件\nの前に追加されるため、.*\n2つの「有効」の前にあるすべての行とその間に少なくとも1つの改行文字があり、その後には次の行の前にあるすべての項目が一致します。

重要な2行がなければ、何も印刷されません。

不要な線を見つけたら、徐々に捨てることは少なくとも美学的に良いです。

sed -En 'H
        /Important/    {x; s/.*[\n](.*Important.*\n.*Important[^\n]*)/\1/; H}
        $              {g; s/.*[\n](.*Important.*\n.*Important[^\n]*).*/\1/p }
'

/Important/変更パターンと保持バッファーを一致させ、最後の目的のブロックのみを保持し、x結果を保持バッファーに戻します。

強調表示し、改行ではなく末尾のクラスと視覚的に一致させるために括弧内に入れます[\n]。もちろん、括弧なしで単一文字クラスを作成することも可能です。

関連情報