Ich habe viele verschiedene CSV-Dateien in einem Ordner (megadrive.txt, snes.txt) wie folgt:
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;;;;;
In diesen CSVs habe ich sehr viele Zeilen und viele haben dasselbe erste Feld. Ich möchte diese Dateien stapelweise verarbeiten und in jeder Datei nur die längste Zeile für jedes erste Feld behalten. Die Ausgabe sollte beispielsweise so aussehen:
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;;;;;
Insbesondere
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
In beiden Datensätzen ist das erste Feld doppelt vorhanden, aber der zweite Eintrag ist länger. Deshalb möchte ich den zweiten Eintrag behalten und alle kürzeren Zeilen mit demselben ersten Feld entfernen.
Wie kann ich das machen?
Antwort1
Ich gehe davon aus, dass Ihre Felder durch definiert sind ;
. Und dass innerhalb eines Felds kein stehen kann ;
. Wenn diese Annahmen zutreffen, können Sie Folgendes tun:
$ 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;;;;;
Dies hat jedoch den Nachteil, dass pro 1. Feld eine Zeile im Speicher abgelegt werden muss, was bei großen Dateien ein Problem darstellen kann. In diesem Fall können Sie stattdessen Folgendes versuchen:
$ 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;;;;;
Sie können jede der Lösungen mit einer einfachen Shell-Schleife auf alle Ihre Dateien anwenden:
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
Oder
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
Antwort2
Versuche es mit 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;;;;;
Beide Sortierungen verwenden ;
als Feldtrennzeichen ( -t';'
). Die erste Sortierung kehrt um ( -r
), so dass die leeren Feldernachdie nicht leeren Felder, und die zweite Sortierung sortiert nach dem ersten Feld ( -k1,1
) und entfernt zusätzliche Zeilen mit demselben ersten Feld ( -u
= uniq), behält aber ansonsten die durch die erste Sortierung festgelegte Reihenfolge bei ( -s
= stabil).
Dies setzt voraus, dass Sie statt der "längsten" Zeile, wie im Titel angegeben, tatsächlich die "vollständigste" Zeile wünschen, d. h. zwischen zwei Zeilen mit demselben ersten Feld hat die kürzere immer einTeilmengeder Felder der längeren Zeile (was meiner Meinung nach der einzige Fall ist, in dem das Verwerfen der kürzeren Zeilen Sinn ergibt). Es wird auch vorausgesetzt, dass Ihre Sortierimplementierung eine -s
(stabile) Option hat: sowohl die GNU- (Linux-) als auch die BSD-Sortierung haben eine.
Wenn Sie dies für einen Stapel von Dateien tun möchten, sollten Sie Folgendes verwenden 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 {} +
Passen Sie die Suchprädikate ( -name
usw.) an und entfernen Sie das echo
„von davor“ nur mv
, wenn Sie bereit sind, Ihre vorhandenen Dateien zu überschreiben.