Angesichts dieser Datei (Anmerkungen sind nicht Teil der Datei, sondern Teil der Erklärung) ...
x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,c,005,b,d,e,y # nb - dupe of row 4
x,dd,006,b,d,e,y
x,c,007,b,d,e,y # nb - dupe of row 4 and 5
x,dd,008,b,d,f,y
x,dd,009,b,d,e,y # nb - dupe of row 6
x,e,010,b,d,f,y
... Ich möchte folgende Ausgabe ableiten:
x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,dd,006,b,d,e,y
x,dd,008,b,d,f,y
x,e,010,b,d,f,y
Wenn Spalte 3 aus der Datei herausgeschnitten wurde und dann uniq über die Datei ausgeführt wurde. Wenn für die verbleibenden Zeilen der Wert für Spalte 3 wieder an der richtigen Stelle hinzugefügt wurde, würde ich das obige Ergebnis erhalten.
Aber ich kämpfe wirklich darum, etwas zu finden, das dies tun würde. Ich würde eine Gelegenheit begrüßen, mehr über die Textverarbeitungsprogramme von Linux zu erfahren.
Leistung: Dateien werden wahrscheinlich nicht größer als 1 MB, und es gibt nur 1 Datei pro Tag.
Ziel: Debian GNU / Linux 7 amd64, 256 MB / Xeon.
Bearbeiten: Beispiel optimiert, da Felder keine feste Breite haben und eine Lösung mit uniq --skip-chars=n
, soweit ich das beurteilen kann, nicht funktioniert.
quelle
uniq
um meine aktualisierte Antwort zu überprüfen. :)Antworten:
Mit
awk
könnten Sie tun:quelle
Der einfachste Weg :
-u
: gibt nur die erste Zeile gleich aus-t,
: Komma als Feldtrennzeichen verwenden-k1,2 -k4
: sortiere nur nach den Feldern 1,2 und 4 und dem RestEine andere Option ist das Neuanordnen der Daten mit
sed
(beachten Sie die GNU-Option-r
) auf beiden Seiten - dies erfordert, dass die Datensätze größtenteils eine feste Länge haben, andernfalls wird es fehlschlagen (und nur kaum merklich):sort
Wenn Sie möchten, können Sie am Ende eine weitere hinzufügen , um sie nach Zahlen-k
zu sortieren (verwenden Sie die Option, um auszuwählen, wie die Sortierung durchgeführt werden soll - z. B. so etwas wiesed -k3 -t,
).In Perl können Sie beispielsweise die Teile, für die Sie die Eindeutigkeit bestimmen möchten, als Schlüssel in einem Hash verwenden (die Werte die vollständigen Zeilen) und nur dann in den Hash einfügen, wenn der Schlüssel noch nicht definiert ist. Dies ist natürlich viel flexibler als das Verwenden
sed
(oderawk
), aber auch das Schreiben (ich bin weit entfernt von einem Perl-Guru, daher ist es sehr wahrscheinlich, dass es viel eleganter gemacht werden kann - siehe andere Antworten für Perl-like Perl-Lösungen):quelle
$lines{$k} = $_ unless $lines{$k};
uniq
's Feldoptionen und konnte nicht, hätte nicht gedacht , zu verwendensort
ist-u
. Übrigens denke ich , dass diessort -u
eine GNU-Erweiterung ist, nicht POSIX, aber dies wird auf Linux-Systemen gut funktionieren.perl
eine) Als totaler Perl-Neuling brauchte ich jedoch ein wenig RTFM, um zu verstehen, was Sie hier machten.%lines
(leicht erkennbar am Prozentzeichen) ist ein assoziatives Array (im Perl-Jargon auch als "Hash-Variable" bezeichnet), das möglicherweise "echte" Zeichenfolgen als Schlüsselkennungen akzeptiert, nicht nur Indexnummern. Dies ist das Element, das für all die wundersame "Magie" verantwortlich ist, die hier getan wird.Ein Weg, dies zu tun mit
awk | sort | uniq | awk
:quelle
Ein einfacherer Perl-Weg wäre:
Das
-a
teilt Felder in das@F
Array auf und-F","
setzt das Feldtrennzeichen auf,
.-n
bedeutet, dass das von-e
in jeder Zeile der Eingabedatei angegebene Skript ausgeführt wird .Die Idee ist, ein Array-Slice (Elemente 0,1 und 3 bis zum Ende des Arrays) zu nehmen, sie zu einem String (
$a
) zu verbinden und diesen String als Hash-Referenz (assoziatives Array) zu verwenden. Sie drucken dann jede Zeile nur, wenn der Hash-Schlüssel zuvor noch nicht gesehen wurde.quelle
ab,c,1,d
unda,bc,2,d
ist das gleiche. Du brauchstjoin(","
. Sie können auch optimieren, indem Sie das$k{$a}++
in denunless() { }
Block verschieben. Und dann wäre das gleichbedeutend mit meinerawk
Lösung ;-).ab,c,1,d
unda,bc,2,d
als identisch - der Vergleich wird an einer rekonstruierten Zeichenfolge durchgeführt (mit den Kommas an den richtigen Stellen).join(","
.$k{$a}++
wenn$a
es schon drin ist%k
. Sie könnten es kürzer machen mit:perl -F, -ane'print if!$k{join",",@F[0,1,3..-1]}++'