ソートせずに、特定の列に基づいてテキストファイル内の一意のデータをフィルタリングします。

ソートせずに、特定の列に基づいてテキストファイル内の一意のデータをフィルタリングします。

次の形式の10-100k行のさまざまなテキストファイルがあります。

"2018-12-07 23:21:32",XX,99,ZZZ,250,REMOVED
"2018-12-07 23:25:17",XX,99,ZZZ,250,AVAILBLE
"2018-12-07 23:29:05",DD,11,AAA,250,REMOVED
"2018-12-07 23:30:00",CH,00,UUU,250,REMOVED
"2018-12-07 23:31:45",MM,33,OOO,250,REMOVED
"2018-12-07 23:46:41",XX,99,ZZZ,250,REMOVED

上記の例では、列2、3、4が同じ3つのレコード(XX、99、ZZZ - 行1/2/6)があることがわかります。最初の2行を削除し、最後の行のみを保持する必要があります。

希望の出力は以下の通りです。

"2018-12-07 23:29:05",DD,11,AAA,250,REMOVED
"2018-12-07 23:30:00",CH,00,UUU,250,REMOVED
"2018-12-07 23:31:45",MM,33,OOO,250,REMOVED
"2018-12-07 23:46:41",XX,99,ZZZ,250,REMOVED

非常に遅く、100k〜ラインのファイルに対してメモリエラーを発生させるPHPスクリプトがあります。

答え1

最後の項目だけを残してすべてを削除するよりも、一連の重複項目のうち最初の項目だけを残してすべてを削除する方が簡単です。次のように試すことができます。

$ tac file | awk -F, '!seen[$2 FS $3 FS $4]++' | tac
"2018-12-07 23:29:05",DD,11,AAA,250,REMOVED
"2018-12-07 23:30:00",CH,00,UUU,250,REMOVED
"2018-12-07 23:31:45",MM,33,OOO,250,REMOVED
"2018-12-07 23:46:41",XX,99,ZZZ,250,REMOVED

答え2

BEGIN { FS = "," }

FNR == NR {
    if (seen[$2,$3,$4])
        delete lines[seen[$2,$3,$4]]

    lines[FNR]
    seen[$2,$3,$4] = FNR

    next
}

FNR in lines

プログラムはawk同じファイルを2回読み取ることを期待しています。ファイルを初めて読み込むと、FNR == NRブロックのみが実行されます。lines配列のキーとして出力する行番号を記憶します。 2番目、3番目、4番目の列の特定の組み合わせを持つ行がすでに表示されている場合は、最も近い行番号をキーとして挿入して前(ステートメント)delete

ファイルの2番目の解析中に何が起こるのかは、配列内の現在の行番号を検索することですlines。見つかった場合は、その行を印刷します。

同じコードの「1行」バージョンを実行する例:

$ awk -F, 'FNR==NR { if(s[$2,$3,$4]) delete l[s[$2,$3,$4]]; l[FNR]; s[$2,$3,$4]=FNR; next}; FNR in l' file file
"2018-12-07 23:29:05",DD,11,AAA,250,REMOVED
"2018-12-07 23:30:00",CH,00,UUU,250,REMOVED
"2018-12-07 23:31:45",MM,33,OOO,250,REMOVED
"2018-12-07 23:46:41",XX,99,ZZZ,250,REMOVED

関連情報