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 col1
mehrfach vor, werden alle dazugehörigen Zeilen gelöscht.
sort
Kann ich es mit Linux oder uniq
etwas 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, gleichFNR
, dem Zeilenzähler pro Datei, ist, registrieren wir, wie oft jeder Wert in Spalte 1 in einem Array vorkommtcnt
(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
awk
Syntax verwendet, die ein Ausdruck außerhalb von Regelblöcken auswertet, der anweist,true
dieawk
aktuelle 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/data
kö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
awk
einzige Lösung
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
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 verwendenNR>1
nach der ersten Zeilecount[$1]++
Zählelement der ersten Spalteline[$1]=$0
LadenzeileEND
nach Dateiendefor ( c in count)
Durchschleifelementif (count[c] ==1)
wenn nur einerprint line[c]
Druckzeilea
undcol[]
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