web-dev-qa-db-ja.com

特定のパターンに一致するテキストをファイルから削除する方法

2つのファイルを比較して、fileAとfileBの違いを確認したいと思います。 fileAはテンプレートファイルのようなもので、fileBは私が比較したいファイルです。違いを見つけたら、その違いをfileCに出力したいと思います。

難しいのは、fileAとfileBに、常に異なるデータ(時刻、日付、ランダムに生成されたIDコード)を持つ特定の(すべてではない)行が含まれていることです。ただし、時間、日付、IDコードのみが異なるfileCに行を出力したくありません。

したがって、私がやりたいのは、fileBで発生する行から時刻、日付、およびIDコードを削除し(これはfileAで手動で実行できます)、fileBと比較して、異なる行をfileCに出力することです。

削除するテキストは常に特定のパターンに従うことに注意してください。したがって、これらのパターンでgrepを使用してテキストを見つけることはできますが、削除する方法がわかりません...

これが私が何を意味するかを示すための2つのファイルの例です:

  • fileB

    qaqa rara
    abc 10:12:25 08/20/2014 123456 def
    ghi fff ddd
    jkl 09:20:40 08/20/2014 978645 dfdf gggg
    
  • fileA

    qaqa rara
    abc 10:32:15 07/15/2014 121456 xxx
    ghi eee ddd
    jkl 10:01:22 07/15/2014 971645 dfdf gggg
    

時刻(例:10:12:25)、日付(例:2014年8月20日)、IDコード(例:123456)を無視して、上記の2つのファイルの違いを見つけ、fileCに出力したい

したがって、異なる2行は2行目と3行目です。1行目は両方のファイルで同じです。 4行目は、時刻、日付、ID情報が削除されている場合、両方のファイルで同じです。

5
didjek

タイムスタンプが一貫してフォーマットされている場合は、ファイルをどのような差分方法で処理する前に(たとえば、sedを使用して)タイムスタンプを取り除くことができます。

diff <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) <(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)

提供された入力ファイルでのテスト:

$ diff \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileA) \
<(sed -E 's|[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{2,4} [0-9]{1,} ||' fileB)
2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd
3
steeldriver
diff \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \\' fileA) \
<(sed -r 's\[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{6} \\' fileB) \
| egrep '^> ' | sed -r 's/^> //' > fileC

説明

OPの質問で与えられた無関係な部分をfileAとfileBから取り除き、これをdiffにフィードします。

diffは、変更されたセクションを前に> "を付けて出力するため、変更以外はすべて無視してください。

最後に、出力から先頭の ">"を取り除き、質問に従ってfileCに保存します。

私はもともと少し違ったやり方をしましたが、ファイルが無関係なセクションで異なる可能性があることに気づきました。そのため、ポストストリップではなく事前にストリップする必要があります。そうしないと、関連部分のみを考慮した場合、diffは実際には変更されていない情報を出力します。

入力例を考えると、cat fileC与える:

abc def
ghi fff ddd

Sedコマンドは、関係のないデータを説明する提供された正規表現を検索し、それを空の文字列に置き換えます。つまり、データを削除します。

1
Ian Macintosh

結果を得る最も簡単なコマンドは以下のとおりです

$ diff <(tr -s "[0-9]、:、/" "" <fileA)<(tr -s "[0-9]、:、/" "" <fileB)

コマンドは非常に単純で、複雑な正規表現もありません。

サンプル出力は以下のようになります

2,3c2,3
< abc xxx
< ghi eee ddd
---
> abc def
> ghi fff ddd

これがあなたが望むものであることを願っています。

1
Jayesh
{   paste -d\| /dev/fd/3 /dev/fd/4 |
    sed '/\([^ ]*\) [0-9:/ ]*\(.*\)|\1 .*\2/d;=' |
    sed 'N;s/\(\n\)\(.*\)|/:\tFILEA: \2\1\tFILEB: /'
} 3<<\FILEA 4<<\FILEB
qaqa rara
abc 10:12:25 08/20/2014 123456 def
ghi fff ddd
jkl 09:20:40 08/20/2014 978645 dfdf gggg
FILEA
qaqa rara
abc 10:32:15 07/15/2014 121456 xxx
ghi eee ddd
jkl 10:01:22 07/15/2014 971645 dfdf gggg
FILEB

出力

2:      FILEA: abc 10:12:25 08/20/2014 123456 def
        FILEB: abc 10:32:15 07/15/2014 121456 xxx
3:      FILEA: ghi fff ddd
        FILEB: ghi eee ddd

時間と日付を取り除く必要はありません-それらを構成するキャラクターが信頼できる限り、それらは大きなハードルではありません。

上記のパイプラインでは、pasteは、最初にFILEBからの対応する行をFILEAの各行の末尾に単一の|セパレーターで追加し、次に結果をstdoutに出力します。

sedはストリームを取得し、以下を比較します。

  • スペースではない0文字以上の最初のシーケンス\1として参照)

  • 次のシーケンスの間に出現するすべての文字:\2として参照)

    • 少なくとも1つの<space>文字、次に次のいずれかの0以上。

    • <space>文字

    • <digit>文字

    • <:colon>文字

    • </slash>文字

  • 行の最後に出現する|文字まで

... |\1.*\2を使用します。それらが一致する場合、sedは行を削除します。そうでない場合は、行番号が前に付いた行を出力します。

最後のsedプロセスは、出力を装うだけです(私は願っています)

0
mikeserv