複数行からのデータフィールドの抽出

複数行からのデータフィールドの抽出

テキストをある形式から別の形式に変換する必要があることがよくあります。一般的な状況は、いくつかの情報を含むログファイルがあり、その情報のサブセットを見つけて非常に具体的な方法でフォーマットすることです。


今、すべての「レコード」が同じ行にある場合、操作は比較的簡単です。

  • grepオプションで、関心のある行のみを一致させるために使用できます。
  • sed目的のフィールドを一致させ、キャプチャし、必要に応じてフォーマットされた結果を出力するために使用されます。

唯一の小さな問題はみんな行なので、sedその行は破棄され、目的の出力のみが印刷されます。しかし、それほど難しくありません。

フィールドキャプチャの問題が十分簡単であればcut十分かもしれませんawk


今実際の質問は次のとおりです。必要なフィールドが複数行にまたがっている場合はどうなりますか?

たとえば、ある人がいるとしましょう。

stat * >LOG.TXT

いいですね。今、私LOG.TXTたちが望むすべての情報を含むファイルができました。しかし、次のようになります。

  File: 'src.7z'
  Size: 269430          Blocks: 528        IO Block: 4096   regular file
Device: 801h/2049d      Inode: 799155      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-07-02 09:01:09.269292914 +0100
Modify: 2020-07-02 09:00:53.237293629 +0100
Change: 2020-07-02 09:00:53.237293629 +0100
 Birth: -

おそらく、私たちは少し似ているように見えるものを生成したいと思うかもしれません。

VerifyFile("src.7z", 269430, 0644);

今、正しい方法そのための最初のステップは、statこのように出力を要求することです!一般的に言えば、いつも実際には、フランケンシュタインのテキストを作成しようとするよりも、ソースプログラムで起動したい形式を生成する方がよいでしょう。残念ながら、人生はいつもそれほど単純ではありません。ログファイルがあり、接続イベントと切断イベントをリンクしたい場合や、ファイルリストがあり、各.cファイルを一致する.oファイルに関連付けたい場合があります。理由が何であれ、最終的にはテキスト修正作業に直面することになります。

それでは、この問題を解決するための最良の方法は何ですか?複数の個別の行からフィールドを収集し、同じ行にマージしながらレコードを別々に保持するにはどうすればよいですか? (つまり入れないでください。すべて巨大な行には同じ記録。 )

この種のタスクを実行する一般的な方法はありません。いくつかの考え:

  • grep正規表現に一致する部分のみ抽出を使用して、目的のフィールドを抽出できます。しかし、抽出する方法はありません。みんな興味深いフィールドを複数回実行しないと、grepフィールド間の相対的な順序が壊れる可能性があります。
  • sed一致するビットだけを抽出することはより困難になりますが、フィールドを同じ相対順序で維持できます。今何とか同じ行にフィールドを収集する必要があります。
  • awkフィールドとレコードの概念があります。私信じる正規表現の一致を実行できます。 (本当に分からないawk。)おそらくそれがこの問題を解決する方法ではないでしょうか?かなり複雑に見えますが。
  • Pythonで何かできますか?一般的にインストールされているようです。正規表現機能があるかどうかわかりません。

通常私は次のことを思い出します。働くしかし、読みやすくメンテナンスが容易ではありませんでした。もっと簡単な方法があったらと思います。

答え1

どちらもここにawk適していますpython。どちらも連想配列(または辞書)をサポートし、両方とも正規表現が組み込まれています。

おそらくこれを処理するいくつかの一般的な方法がありますが、私は利用可能な実際の入出力例に基づいて最適化する傾向があります。これがstat解決された問題ですawk

$ cat kv.awk 
/File:/ {
    f = $2
}

/Size:/ {
    info[f] = $2
}

/Access.*Uid/ {
    gsub(/\(|\/.*/, "", $2)
    info[f] = info[f] OFS $2
}

END {
    for (k in info) {
        print "VerifyFile(\"" k "\"", info[k] ");"
    }
}

ファイル名は次のように使用されます。関連するすべてを集めてください。最後に、必要なすべての形式で結果を表示できます。GNU awk多次元配列もサポートされます。

実行例は次のとおりです。

$ stat ip.txt test.txt > file.txt

$ awk -v OFS=', ' -f kv.awk file.txt
VerifyFile("'test.txt'", 254, 0664);
VerifyFile("'ip.txt'", 301, 0664);

勉強することをawkお勧めしますhttps://www.gnu.org/software/gawk/manual/gawk.html他の実装に関する詳細が取り上げGNU awkられ、言及されています。

関連情報