Entfernen Sie die gesamte Zeile in einer Datei, wenn die erste Spalte wiederholt wird

7

Ich habe eine Datei mit zwei Spalten und 10 Millionen Zeilen. Die erste Spalte enthält viele wiederholte Werte, aber in Spalte 2 gibt es einen eindeutigen Wert. Ich möchte die wiederholten Zeilen entfernen und nur eine verwenden awk. Hinweis: Die Datei ist nach Werten in Spalte 1 sortiert. Beispiel:

1.123 -4.0
2.234 -3.5
2.234 -3.1
2.234 -2.0
4.432 0.0
5.123 +0.2
8.654 +0.5
8.654 +0.8
8.654 +0.9
.
.
.
.

Erwartete Ausgabe

1.123 -4.0
2.234 -3.5
4.432 0.0
5.123 +0.2
8.654 +0.5
.
.
.
.
Nilesh
quelle
Sie können diese aktuelle Frage nützlich finden unix.stackexchange.com/q/159695/65304
steeldriver
3
Einfach:sort -buk1,1
Stéphane Chazelas

Antworten:

14

Ein paar Möglichkeiten:

  1. awk

    awk '!a[$1]++' file

    Dies ist eine sehr komprimierte Schreibweise:

    awk '{if(! a[$1]){print; a[$1]++}}' file

    Wenn sich das aktuelle erste Feld ( $1) nicht im aArray befindet, drucken Sie die Zeile und fügen Sie das erste Feld hinzu a. Wenn wir das nächste Mal dieses Feld sehen, befindet es sich im Array und wird daher nicht gedruckt.

  2. Perl

    perl -ane '$k{$F[0]}++ or print' file

    oder

    perl -ane 'print if !$k{$F[0]}++' file

    Dies ist im Grunde das gleiche wie das awkeine. Die -nUrsachen perl die Eingabedatei Zeile für Zeile zu lesen und das Skript bereitgestellt durch Anwendung -eauf jeder Zeile. Das -ateilt automatisch jede Zeile in Leerzeichen und speichert die resultierenden Felder im @FArray. Schließlich wird das erste Feld zum %kHash hinzugefügt, und wenn es noch nicht vorhanden ist, wird die Zeile gedruckt. Das gleiche könnte geschrieben werden als

    perl -e 'while(<>){
                @F=split(/\s+/); 
                print unless defined($k{$F[0]}); 
                $k{$F[0]}++;
             }' file
    
  3. Coreutils

    rev file | uniq -f 1 | rev

    Diese Methode funktioniert, indem zuerst die Zeilen umgekehrt werden, fileso dass, wenn eine Zeile 12 345 ist, sie jetzt 543 21 ist. Wir verwenden dann uniq -f 1, um das erste Feld zu ignorieren, dh die Spalte, in der sich 543 befindet. Es gibt Felder darin file. Wenn Sie uniqhier verwenden, werden doppelte Zeilen herausgefiltert, wobei jeweils nur eine Zeile beibehalten wird. Zuletzt setzen wir die Zeilen mit einer anderen Umkehrung wieder in ihre ursprüngliche Reihenfolge.

  4. GNU Art (wie vorgeschlagen von @ StéphaneChazelas)

    sort -buk1,1

    Das -bFlag ignoriert führende Leerzeichen und die -uMittelwerte drucken nur eindeutige Felder. Das Schlaue ist das -k1,1. Das -kFlag setzt das Feld zum Sortieren. Es nimmt das allgemeine Format an, bei -k POS1[,POS2]dem POS1beim Sortieren nur Felder über POS2 betrachtet werden. Also -k1,1heißt nur auf das 1. Feld schauen. Abhängig von Ihren Daten möchten Sie möglicherweise auch eine der folgenden Optionen hinzufügen:

     -g, --general-numeric-sort
          compare according to general numerical value
     -n, --numeric-sort
          compare according to string numerical value
    
terdon
quelle
Hmm, dafür revwird es verwendet ... Danke.
Lgeorget
Ich versuche die oben genannte awk-Lösung und erhalte die Meldung a [: Ereignis nicht gefunden.
Nilesh
Auch nach dieser Meldung, wenn ich die Taste drücke, wird der Befehl in awk '$ 1] ++' Dateiname
geändert
@Nilesh haben Sie den genauen Befehl kopiert oder doppelte Anführungszeichen ( ") anstelle von einfachen Anführungszeichen ( ') verwendet?
Terdon
1
@ 1_CR guter Punkt, danke. Ich habe die Beschreibungen der beiden relevanten Optionen hinzugefügt. Ich kenne die Details nicht, aber einige schnelle Tests sort -ulegen nahe, dass vorsortierte Daten erheblich schneller sind, ja.
Terdon
4

Wenn die erste Spalte immer 5 Zeichen lang ist, können Sie einfach Folgendes verwenden uniq:

uniq -w 5 file

Wenn nicht, verwenden Sie awk:

awk '$1!=a{print $0; a=$1}' file

Der erste wäre definitiv schneller mit einer riesigen Datei.

Chaos
quelle