Entfernen Sie Duplikate in jeder Zeile einer Datei

1965
Arash

Wie kann ich beispielsweise Duplikate in jeder Zeile entfernen?

1 1 1 2 1 2 3 5 5 4 1 2 3 3 

Ich möchte diese Ausgabe erhalten:

1 2 3  5 4 1 2 3 

Es gibt viele Zeilen (100.000) und in jeder Zeile möchte ich eindeutige Werte. Perl ist vielleicht das schnellste, aber wie kann ich das in Perl oder Bash machen?

7

3 Antworten auf die Frage

12
nerdwaller

Hier ist eine Option mit awk:

awk '{ while(++i<=NF) printf (!a[$i]++) ? $i FS : ""; i=split("",a); print ""}' infile > outfile 

Bearbeiten mit Kommentaren aktualisiert:

  1. while (++i<=NF)

    Initialisiert die while-Schleife, wobei "i" vorweggenommen wird, da $ 0 die vollständige Zeile in awk ist.

    Es beginnt also bei $ 1 (erstes Feld). Schleife durch die Zeile bis zum Ende (kleiner oder gleich "NF", das in "awk" für "Number of Fields" eingebaut ist). Das Standardfeldtrennzeichen ist ein Leerzeichen. Sie können das Standardtrennzeichen leicht ändern.

  2. printf (!a[$i]++) ? $i FS : ""

    Dies ist eine ternäre Operation .

    Wenn sich die Eingabe nicht im Array befindet !a[$i]++, wird $ i ausgegeben. Wenn ja, wird "" ausgegeben. (Sie können das entfernen !und umkehren, $i FS : ""wenn Sie es auf diese Weise nicht mögen).

  3. i=split("",a)

    Normalerweise ist das ein Nullsplit. In diesem Fall setzt ich I für die nächste Zeile zurück.

  4. print ""

    beendet die Zeile für die Ausgabe (nicht warum eigentlich 100%), andernfalls würden Sie Folgendes ausgeben:

    1 2 3 5 4 1 2 3 anstatt
    1 2 3
    5 4 1 2 3

Um aktuellen und zukünftigen Lesern zu helfen, versuchen Sie, die Antworten in gewissem Umfang zu dokumentieren. Dies ist kompakt und effizient, aber es ist für jemanden, der nicht sehr an "awk" gewöhnt ist, ziemlich unlesbar, da er sich auf Test- und Operationsreihenfolge, den ternären Operator, die "split" ("", a) "quirk" und ein Array (und dessen Rückgabewert zum Zurücksetzen von "i") und den Sondervariablen "NF" und "FS". Eine solche Erklärung macht die Antwort noch besser! Daniel Andersson vor 11 Jahren 5
@DanielAndersson Meine Entschuldigung, faul zu sein, aktualisiert. Vielen Dank! nerdwaller vor 11 Jahren 0
nerdwaller: der Grund, warum Sie 1 2 3 5 4 1 2 3 ohne Schritt 4 erhalten, ist, dass Ihre gesamte Ausgabe über printf erfolgt, ohne \ n jemals in ... tink vor 11 Jahren 1
Schritt 2 funktioniert, da der Feldwert mit dem Index der aktuellen Nummer inkrementiert wird. Wenn dieser Index leer war, gibt der Test "! False" zurück, und das Inkrement wird nach dem Vergleich ausgeführt. Wenn die Schleife das nächste Mal dieselbe Zahl findet, gibt der Vergleich "! True" zurück, da der Wert, der dem Index entspricht, das letzte Mal auf einen Wert gesetzt wurde. Das Feld wird erneut inkrementiert, aber diese "Gesamtanzahl" wird später nicht verwendet (es tut jedoch nicht weh). Daniel Andersson vor 11 Jahren 0
In Schritt 3 wird das Array "a" für die nächste Zeileniteration gelöscht. `split (", a) `ist eine Abkürzung zum Löschen eines Arrays` a` (siehe [die Dokumentation] (http://www.gnu.org/software/gawk/manual/html_node/Delete.html#fn- 1) für eine Mitteilung). Als Nebeneffekt gibt diese Operation auch '0' zurück, und da 'i' für die nächste Iteration auf '0' gesetzt werden sollte, wird statt eines separaten `i = 0 der Aufruf 'split ()' anstelle der Zuweisung verwendet `Aufruf, Speichern einiger Zeichen (möglicherweise auf Kosten der Lesbarkeit). Daniel Andersson vor 11 Jahren 0
5
slhck

Da rubykommt mit jeder Linux-Distribution, die ich kenne:

ruby -e 'STDIN.readlines.each { |l| l.split(" ").uniq.each { |e| print "# " }; print "\n" }' < test 

Hier testist die Datei, die die Elemente enthält.

Um zu erklären, was dieser Befehl bewirkt - obwohl Ruby fast von links nach rechts gelesen werden kann:

  • Lesen Sie die Eingabe (die von < testIhrer Shell kommt)
  • Gehen Sie jede Zeile der Eingabe durch
  • Teilen Sie die Zeile basierend auf einem Leerzeichen in ein Array ( split(" ")).
  • Holen Sie sich die einzigartigen Elemente aus diesem Array (in der Reihenfolge)
  • Drucken Sie für jedes eindeutige Element ein Leerzeichen ( print "# ").
  • Wenn Sie mit den einzigartigen Elementen fertig sind, drucken Sie eine neue Zeile
2
glenn jackman

Not pure bash, but ...:

while read line; do printf "%s\n" $line | sort -u | tr '\n' ' ' echo '' done < file 

The lines will be sorted as a byproduct.