最初の値に基づいて冗長CSVを削除し、重複の間に最も長い行を維持します。

最初の値に基づいて冗長CSVを削除し、重複の間に最も長い行を維持します。

私のフォルダには、次のようなさまざまなcsvファイル(megadrive.txt、snes.txt)があります。

Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;;;;;;;;;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;

このCSVには多数の行があり、多くの行に同じ最初のフィールドがあります。これらのファイルをバッチ処理し、各ファイル内の各最初のフィールドの最長行だけを維持したいと思います。たとえば、出力は次のようになります。

Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;

特に

Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;

どちらのレコードにも重複した最初のフィールドがありますが、2番目の項目が長いため、2番目の項目を最後に保持し、同じ最初のフィールドを持つ短い行をすべて削除したいと思います。

どうすればいいですか?

答え1

あなたのフィールドがによって定義されているとします;。そして;現場にはコンテンツがありません。これらの仮定が成立している場合は、次のことができます。

$ awk -F';' '{if(!a[$1]||length($0)>length(a[$1])){a[$1]=$0}}END{for(i in a){print a[i]}}' file.txt
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;

しかし、これはメモリの最初のフィールドごとに1行を保存する必要があるという欠点があり、これは大容量ファイルの場合に問題になる可能性があります。その場合は、次のことを試すことができます。

$ awk '{print length($0)";"$0}' file.txt | sort -t';' -rnk1,1 | awk -F';' '++a[$2]==1' | cut -d';' -f2-
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;;;;;;;;;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;

単純なシェルループを使用して、両方のソリューションのいずれかをすべてのファイルに適用できます。

for f in *txt; do 
    awk -F';' '{if(!a[$1]||length($0)>length(a[$1])){a[$1]=$0}}END{for(i in a){print a[i]}}' "$f" > "$f".fixed
done

または

for f in *txt; do 
    awk '{print length($0)";"$0}' file.txt | sort -t';' -rnk1,1 | 
        awk -F';' '++a[$2]==1' | cut -d';' -f2- > "$f".fixed
done

答え2

以下を試してくださいsort(1)

sort -rt';' filename | sort -t';' -usk1,1

Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;

;どちらのソートもフィールド区切り記号()として使用されます-t';'。最初は逆方向(-r)でソートされ、空のフィールドが表示されます。後ろにNULL以外のフィールドの場合、2番目のソートは最初のフィールド(= uniq)に基づいてソートし、同じ最初のフィールド(= uniq)を持つ余分な行を削除しますが、そうでない場合は最初の-k1,1ソート(= stable)-uで設定します。順序を維持します。-s

これは、タイトルが示すように、「最も長い」行ではなく「最も完全な」行を実際に望んでいると仮定します。最初のフィールドが同じ2行の間では、常に短い方がサブセット長いフィールドのフィールド(IMHOは、より短い行を削除するのが適切な唯一のケースです)。また、ソート実装に-sGNU(Linux)およびBSDソートという(安定した)オプションがあるとします。

ファイル配置でこれを行うには、次のものを使用する必要がありますfind

find dir -type f -name '*.txt' \
    -exec sh -c 'for f; do sort -rt";" "$f" |
    sort -t";" -usk1,1 > "$f.new" && echo mv "$f.new" "$f"; done' sh {} +

ルックアップ条件などを調整し、-name既存のファイルを削除する準備ができた場合にのみecho古い条件を削除してください。mv

関連情報