Elimine los duplicados csv según el primer valor manteniendo la línea más larga entre duplicados

Elimine los duplicados csv según el primer valor manteniendo la línea más larga entre duplicados

Tengo muchos archivos csv diferentes en una carpeta (megadrive.txt, snes.txt) como esta:

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;;;;;

En estos CSV, tengo muchísimas líneas y muchas tienen el mismo primer campo. Quiero procesar estos archivos por lotes y, en cada archivo, conservar solo la línea más larga para cada primer campo. Por ejemplo, la salida debería ser:

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;;;;;

En particular

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

Ambos registros tienen el primer campo duplicado pero la segunda entrada es más larga, por lo que me gustaría mantener la segunda entrada y eliminar todas las líneas más cortas con el mismo primer campo.

¿Cómo puedo hacer esto?

Respuesta1

Supongo que sus campos están definidos por ;. Y eso que no puede haber ;dentro de un campo. Si esas suposiciones son ciertas, puedes hacer:

$ 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;;;;;

Sin embargo, esto tiene el inconveniente de necesitar almacenar una línea por primer campo en la memoria y eso podría ser un problema para archivos grandes. Si es así, puedes probar esto en su lugar:

$ 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;;;;;

Puede aplicar cualquiera de las soluciones a todos sus archivos con un simple bucle de shell:

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

O

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

Respuesta2

Prueba con 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;;;;;

Ambos tipos utilizarán ;como delimitador de campo ( -t';'). El primero invertirá la ordenación ( -r), de modo que los campos vacíos vengandespuéslos campos no vacíos, y la segunda clasificación ordenará por el primer campo ( -k1,1) y eliminará las líneas adicionales con el mismo primer campo ( -u= uniq), pero por lo demás mantendrá el orden establecido por la primera clasificación ( -s= estable).

Esto supone que en lugar de la línea "más larga" como dice el título, en realidad desea la línea "más completa", es decir. entre dos líneas con el mismo primer campo, la más corta siempre tiene unsubconjuntode los campos del más largo (que es el único caso en el que descartar las líneas más cortas puede tener algún sentido en mi humilde opinión). También supone que su implementación de clasificación tiene una -sopción (estable): tanto la clasificación GNU (Linux) como la BSD la tienen.

Si desea hacerlo en un lote de archivos, debe usar 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 {} +

Ajuste los predicados de búsqueda ( -name, etc.) y solo elimine el echode antes mvsi está listo para destruir sus archivos existentes.

información relacionada