重複間の最長の行を維持しながら、最初の値に基づいて重複した csv を削除します。

重複間の最長の行を維持しながら、最初の値に基づいて重複した csv を削除します。

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

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)で、空のフィールドが空でないフィールドでソートし、2 番目のソートでは最初のフィールド ( -k1,1) でソートし、同じ最初のフィールドを持つ余分な行 ( -u= uniq) を削除しますが、それ以外は最初のソートで設定された順序を維持します ( -s= stable)。

これは、タイトルにあるように「最も長い」行ではなく、実際には「最も完全な」行、つまり同じ最初のフィールドを持つ2つの行のうち、短い方の行には常にサブセット長い方のフィールドの (短い方の行を破棄することが意味をなす唯一のケースだと私は思います)。また、ソートの実装に-s(安定した) オプションがあることも前提としています。GNU (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

関連情報