Ich möchte aus einer bestimmten Spalte ($2 im Beispiel) die doppelten Felder (kommagetrennt) entfernen.
Eingabedatei:
A 1,2,3,4
B 4,5,6,3
C 2,15
Erwartete Ausgabe:
A 1,2,3,4
B 5,6
C 15
Akzeptierte Antwort:
perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e'
Sie können das obige wie folgt ausführen:
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
Wie es funktioniert
Erster Aufruf von perl
mit -lpe
macht die folgenden 3 Dinge.
-l[octal]
Zeilenende-Verarbeitung aktivieren, gibt Zeilenabschlusszeichen an-p
Schleife wie -n annehmen, aber auch Zeile drucken, wie sed-e program
eine Programmzeile (mehrere -e erlaubt, Programmdatei weglassen)
Dies nimmt im Wesentlichen die Datei auf, entfernt die Zeilenumbrüche, bearbeitet eine Zeile und fügt dann ein Zeilenumbruchzeichen wieder daran an, wenn es fertig ist. Es geht also nur durch die Datei und führt unseren Perl-Code nacheinander gegeneinander aus.
Was den eigentlichen Perl-Code betrifft:
s
bedeutet ein Leerzeichen (die fünf Zeichen[ fnrt]
undv
in neueren Versionen vonperl
, wie[[:space:]]
).K
Behalte das Zeug links vom K, füge es nicht in $&
ein S+
ein oder mehrere Zeichen nicht im Satz [ fnrtv]
Der join ",",
nimmt die Ergebnisse und fügt jedes Feld wieder zusammen, sodass es durch ein Komma getrennt ist.
Der split ",", $&
nimmt die Übereinstimmungen, die von S+
gefunden wurden und teilen Sie sie nur in die Felder auf, ohne das Komma.
Der grep {!$seen{$_}++}
nimmt die Nummer jedes Felds und fügt sie dem Hash hinzu, $seen{}
wobei die Nummer jedes Felds $_
ist während wir durch jeden von ihnen gehen. Jedes Mal, wenn eine Feldnummer „gesehen“ wird, wird sie über den ++
gezählt Operator, $seen{$_}++
.
Der grep{!$seen{$_}++}
gibt einen Feldwert zurück, wenn er nur einmal gesehen wurde.
Geändert, um zu sehen, was passiert
Wenn Sie diese modifizierte Abscheulichkeit verwenden, können Sie sehen, was los ist, wenn dieser Perl-Einzeiler sich über die Zeilen der Datei bewegt.
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile
keys: 4 1 3 2 | vals: 1 1 1 1
A 1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B 5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C 15
Dies zeigt Ihnen den Inhalt von $seen{}
am Ende der Verarbeitung einer Zeile aus der Datei. Nehmen wir die zweite Zeile der Datei.
B 4,5,6,3
Und hier ist, was meine modifizierte Version diese Zeile als zeigt:
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
Das bedeutet also, dass wir Feld Nr. 6 (1 Mal), Feld Nr. 4 (2 Mal) usw. und Feld Nr. 5 (1 Mal) gesehen haben. Wenn also grep{...}
gibt die Ergebnisse zurück, es werden nur Ergebnisse aus diesem Array zurückgegeben, wenn es in dieser Zeile vorhanden war (4,5,6,3) und wenn wir es nur einmal gesehen haben (6,1,15,5). Die Schnittmenge dieser 2 Listen ist (5,6) und das wird von grep
zurückgegeben .
Referenzen
- perlre – perldoc.perl.org