Verwenden von awk zum Schreiben ausschließlich von Duplikaten

Verwenden von awk zum Schreiben ausschließlich von Duplikaten

Das Löschen von Duplikaten mit awk ist ziemlich üblich und einfach. Aber ich muss nur die Zeilen drucken, die dupliziert sind, wenn wir nur eine Spalte vergleichen. Ich habe diesen Befehl ausprobiert:

awk 'seen[$2]++'

aber wie Sie sehen, hat es Mängel. Es druckt Duplikate, aber erst seit ihrem zweiten Auftreten. Ich habe gerade erst angefangen, mich an Unix und Bash zu gewöhnen, daher wäre es toll, wenn Sie mir die Lösung erklären könnten.

Antwort1

Ich sehe zwei Möglichkeiten, dies zu tun:

  1. Durchlaufen Sie die Datei zweimal:

    Zählen Sie in der ersten Iteration, wie oft jedes $2 vorkommt.
    Drucken Sie in der zweiten Iteration nur die Zeilen, bei denen die Anzahl größer als 1 ist.

    awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
    
  2. mit einer einzigen Iteration der Daten:

    Sie müssen zählen, wie oft jeder $2 vorkommt,UndMerken Sie sich, welche Zeilen für jeweils 2 $ aufgetreten sind.

    Diese Antwort verwendet GNU awk für Arrays von Arrays. Die Reihenfolge der Ausgabe stimmt wahrscheinlich nicht mit der der Eingabedaten überein. Außerdem muss die gesamte Datei im Speicher gespeichert werden.

    gawk '
        { lines[$2][++count[$2]] = $0 }
        END {
            for (x in lines)
                if (count[x] > 1)
                    for (i=1; i<=count[x]; i++)
                        print lines[x][i]
        }
    ' file
    

Getestet mit Eingabedatei:

$ cat file
a b
b b
c b
a c
a d
b d
a e

und erwartete Ausgabe

a b
b b
c b
a d
b d

Antwort2

Unter Verwendung derselben Beispieleingabe wieGlenn Jackman's Antwort

$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
  • !seen[$2]++wenn $2es vorher noch nicht aufgetreten ist:
    • fl[$2]=$0Speichern Sie diese erste Zeile. Ich bin davon ausgegangen, dass die Eingabe nicht sortiert ist und Duplikate überall in der Datei auftreten können. Daher speichere ich sie basierend auf $2 statt nur einer temporären Variable.
    • c[$2]=1In ähnlicher Weise initialisieren Sie die Variable count mit 1
  • $2 in seenwenn $2schon einmal aufgetreten:
    • if(c[$2]--){print fl[$2]}zuerst die vorherige Zeile drucken, der Zähler wird dekrementiert, so dass die Bedingung für nachfolgende Übereinstimmungen fehlschlägt
    • printdann drucken Sie die aktuelle Zeile


Mit einigen anderen Eingaben

$ cat ip.txt 
6.2  : 897 : bar
3.1  : 32  : foo
1.2  : 123 : xyz
2.3  : 32  : baz
7.5  : 897 : boo

$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt 
3.1  : 32  : foo
2.3  : 32  : baz
6.2  : 897 : bar
7.5  : 897 : boo

Beachten Sie, dass die Reihenfolge davon abhängt, wie Duplikate auftreten

Antwort3

Wenn Sie zweimal über dieselbe Datei iterieren, können Sie Zeilennummern als praktische Indizes verwenden. Dies kann zu einer übersichtlicheren Logik führen.

awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file

Ich habe bei meiner Antwort auf diese Frage einen ähnlichen Trick verwendet:


Dieser Trick beruht darauf, dass Awk eine Variable einfach durch Referenzieren erstellt und die index in arraynameKonstruktion „true“ oder „false“ zurückgibt, je nachdem, ob ein Array-Element mit dem angegebenen Index erstellt wurde.

verwandte Informationen