Wie kann man mit der Linux-Eingabeaufforderung nur eindeutige Zeilen basierend auf einer Spalte auswählen?

Wie kann man mit der Linux-Eingabeaufforderung nur eindeutige Zeilen basierend auf einer Spalte auswählen?

Hier ist mein Datensatz:

col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k

Erwartete Ausgabe:

f,g,h
x,t,k

Auswahlkriterium:

Kommt etwas col1mehrfach vor, werden alle dazugehörigen Zeilen gelöscht.

sortKann ich es mit Linux oder uniqetwas anderem lösen ?

Antwort1

Hier ist ein „nicht-puffernder“ (1) Zwei-Durchgänge-Ansatz mit awk(funktioniert nur bei regulären Dateien).

awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv 

Dadurch wird die Datei zweimal verarbeitet und daher zweimal als Argument in der Befehlszeile angegeben.

  • Das Argument -F','legt den Feldtrenner auf fest ,.
  • Im ersten Durchgang, wenn NR, der globale Zeilenzähler, gleich FNR, dem Zeilenzähler pro Datei, ist, registrieren wir, wie oft jeder Wert in Spalte 1 in einem Array vorkommt cnt(das den Wert als „Array-Index“ verwendet), überspringen die Verarbeitung jedoch sofort zur nächsten Zeile.
  • Im zweiten Durchgang prüfen wir, ob der Vorkommenszähler für den aktuellen Wert der ersten Spalte genau 1 beträgt und die Zeilennummer innerhalb der Datei größer als 1 ist (um den Header zu überspringen). Nur wenn das zutrifft, wird die aktuelle Zeile gedruckt. Dabei wird die awkSyntax verwendet, die ein Ausdruck außerhalb von Regelblöcken auswertet, der anweist, truedie awkaktuelle Zeile zu drucken.

(1) Als Reaktion auf einen Kommentar, den ichnicht pufferndin Anführungszeichen, da die Lösung einige Daten aus der Datei vorübergehend im RAM speichert,tutmit RAM-Nutzung kommen. Allerdings wird es nicht den Dateiinhalt wörtlich speichernZusätzlichzu allen anderen Scroll-Speicherdaten im RAM (dieICHwürde ich als "Pufferung" im eigentlichen Sinne bezeichnen).

Antwort2

Vorausgesetzt, die Datei ist vorhanden, /tmp/datakönnen Sie dies mit einem Perl-Einzeiler tun:

perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data

Oder besser lesbar...:

while(<STDIN>) { #loop through all lines in the input and put the lines in "$_"
  /(^\S+?),/; #Everything before the first "," now ends up in "$1"
  $show->{$1} = $_; #a hash will be created with as keys the "$1" and as values the "$_"
  $count->{$1}++; #In the hash $count the number of occurrences will be increased everytime the same $1 appears
}
foreach(keys %$show) { #loop trough all lines
  print $show->{$_} if($count->{$_} == 1); #only print them if they occur once
}

Antwort3

awkeinzige Lösung

  1. keine Ordnung halten

    awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} 
       END { for ( c in count) if (count[c] ==1) print line[c]}' data
    
  2. für Ordnung sorgen

    awk -F, 'NR>1 { row[a]=$0; col[a]=$1; count[$1]++; ++a; } 
       END { for (i=0; i<a; ++i) if (count[col[i]]==1) print row[i]; }' data
    

Wo

  • -F,,sagen Sie awk, es als Trennzeichen zu verwenden

  • NR>1 nach der ersten Zeile

  • count[$1]++Zählelement der ersten Spalte

  • line[$1]=$0 Ladenzeile

  • ENDnach Dateiende

  • for ( c in count)Durchschleifelement

  • if (count[c] ==1)wenn nur einer

  • print line[c]Druckzeile

  • aund col[]werden verwendet, um die Zeilenreihenfolge in der reihenfolgeerhaltenden Variante zu speichern.

Dies kann einzeilig sein, ich falte es der Lesbarkeit halber zusammen

Antwort4

Dekorieren/Sortieren/Verwenden/Entfernen der Dekoration mithilfe einer beliebigen Version der obligatorischen POSIX-Tools und aller Zeichen in Ihrer Eingabe (es sei denn, Ihre Eingabe ist tatsächlich eine CSV-Datei mit Anführungszeichen, die Kommas und/oder Zeilenumbrüche enthalten können, aber dann würden auch alle anderen Antworten fehlschlagen) und Beibehalten der Reihenfolge der Eingabezeilen für die Ausgabe und nur einmaliges Öffnen der Eingabe, damit es funktioniert, wenn die Eingabe aus einer Pipe oder einer Datei kommt und ohne die gesamte Eingabe im Speicher zu speichern:

$ awk 'BEGIN{FS=OFS=","} NR>1{print ++cnt[$1], NR, $0}' file |
    sort -nt, -k1,1r -k2,2 |
    awk -F, '(!seen[$3]++) && ($1==1)' |
    cut -d, -f3-
f,g,h
x,t,k

verwandte Informationen