Doppelte Zeilen in einer Datei identifizieren, ohne sie zu löschen?


11

Ich habe meine Referenzen als Textdatei mit einer langen Liste von Einträgen und jedem hat zwei (oder mehr) Felder.

Die erste Spalte ist die URL der Referenz. Die zweite Spalte ist der Titel, der je nach Eingabe leicht variieren kann. Gleiches gilt für das dritte Feld, das vorhanden sein kann oder nicht.

Ich möchte Einträge identifizieren, aber nicht entfernen, bei denen das erste Feld (Referenz-URL) identisch ist. Ich weiß davon, sort -k1,1 -uaber das wird automatisch (nicht interaktiv) alle außer dem ersten Treffer entfernen. Gibt es eine Möglichkeit, mich einfach zu informieren, damit ich auswählen kann, welche ich behalten möchte?

Im folgenden Auszug aus drei Zeilen mit demselben ersten Feld ( http://unix.stackexchange.com/questions/49569/) möchte ich Zeile 2 beibehalten, da sie zusätzliche Tags (Sortieren, CLI) enthält und die Zeilen 1 und 3 löscht:

http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Gibt es ein Programm zur Identifizierung solcher "Duplikate"? Dann kann ich manuell bereinigen, indem ich die Zeilen 1 und 3 persönlich lösche.


Ich verstehe Ihr Beispiel nicht ganz ... könnten Sie eine vereinfachte Version der Eingabe und der erwarteten Ausgabe angeben?
Oli

Bitte sehen Sie, ob es jetzt klarer ist?
DK Bose

Antworten:


9

Wenn ich Ihre Frage verstehe, denke ich, dass Sie etwas brauchen wie:

for dup in $(sort -k1,1 -u file.txt | cut -d' ' -f1); do grep -n -- "$dup" file.txt; done

oder:

for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n -- "$dup" file.txt; done

Wo file.txtist Ihre Datei mit Daten über Sie interessiert.

In der Ausgabe sehen Sie die Anzahl der Zeilen und Zeilen, in denen das erste Feld zweimal oder mehrmals gefunden wird.


3
Danke: cut -d " " -f1 file.txt | uniq -dgibt mir sogar schöne Ergebnisse.
DK Bose

@DKBose Es gibt wahrscheinlich mehr Möglichkeiten, aber ich wollte und Ihren Befehl auch verwenden.
Radu Rădeanu

Vielen Dank. Der zweite Befehl ist der, den ich mag. Sie können die erste entfernen. Und wenn Sie den Code erklären, wäre das auch schön :)
DK Bose

10

Dies ist ein klassisches Problem, das mit dem uniqBefehl gelöst werden kann . uniqkann doppelte aufeinanderfolgende Zeilen erkennen und Duplikate entfernen ( -u, --unique) oder nur Duplikate behalten ( -d, --repeated).

Da die Reihenfolge doppelter Zeilen für Sie nicht wichtig ist, sollten Sie sie zuerst sortieren. Verwenden Sie dann uniq, um nur eindeutige Zeilen zu drucken:

sort yourfile.txt | uniq -u

Es gibt auch eine -c( --count) Option, die die Anzahl der Duplikate für die -dOption druckt . uniqEinzelheiten finden Sie auf der Handbuchseite von .


Wenn Sie sich wirklich nicht für die Teile nach dem ersten Feld interessieren, können Sie den folgenden Befehl verwenden, um doppelte Schlüssel zu finden und jede Zeilennummer dafür zu drucken (fügen Sie eine andere hinzu | sort -n, damit die Ausgabe nach Zeilen sortiert wird):

 cut -d ' ' -f1 .bash_history | nl | sort -k2 | uniq -s8 -D

Da Sie doppelte Zeilen sehen möchten (indem Sie das erste Feld als Schlüssel verwenden), können Sie diese nicht direkt verwenden uniq. Das Problem, das die Automatisierung erschwert, besteht darin, dass die Titelteile variieren, ein Programm jedoch nicht automatisch bestimmen kann, welcher Titel als endgültiger Titel betrachtet werden soll.

Hier ist ein AWK-Skript (speichern in script.awk), das Ihre Textdatei als Eingabe verwendet und alle doppelten Zeilen druckt, damit Sie entscheiden können, welche gelöscht werden sollen. ( awk -f script.awk yourfile.txt)

#!/usr/bin/awk -f
{
    # Store the line ($0) grouped per URL ($1) with line number (NR) as key
    lines[$1][NR] = $0;
}
END {
    for (url in lines) {
        # find lines that have the URL occur multiple times
        if (length(lines[url]) > 1) {
            for (lineno in lines[url]) {
                # Print duplicate line for decision purposes
                print lines[url][lineno];
                # Alternative: print line number and line
                #print lineno, lines[url][lineno];
            }
        }
    }
}

Ich denke, das ist nah an dem, was ich will, aber ich brauche das Gegenteil von `-f, --skip-fields = N (vermeide es, die ersten N Felder zu vergleichen). Mit anderen Worten, ich möchte, dass nur das erste Feld, die URLs, berücksichtigt werden.
DK Bose

@DKBose Es gibt eine -w( --check-chars) Option, um auf eine feste Anzahl von Zeichen zu beschränken, aber in Ihrem Beispiel haben Sie variable erste Felder. Da die uniqFeldauswahl nicht unterstützt wird, müssen Sie eine Problemumgehung verwenden. Ich werde ein AWK-Beispiel hinzufügen, da dies einfacher ist.
Lekensteyn

Ja, ich habe nur geschaut, -waber die Länge des ersten Feldes ist variabel :(
DK Bose

@DKBose Bitte sehen Sie die neueste Bearbeitung
Lekensteyn

1
Ich erhalte awk: script.awk: Zeile 4: Syntaxfehler bei oder in der Nähe von [awk: script.awk: Zeile 10: Syntaxfehler bei oder in der Nähe von [awk: script.awk: Zeile 18: Syntaxfehler bei oder in der Nähe}
DK Bose

2

Wenn ich das richtig lese, brauchen Sie nur so etwas

awk '{print $1}' file | sort | uniq -c | 
    while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done

Dadurch wird die Nummer der Zeile ausgedruckt, die den Betrüger enthält, und die Zeile selbst. Beispiel: Verwenden dieser Datei:

foo bar baz
http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
bar foo baz
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
baz foo bar
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Es wird diese Ausgabe erzeugen:

2:http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
4:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
6:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Sie können nur die Nummer der Zeile drucken

awk '{print $1}' file | sort | uniq -c | 
 while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 1

Und um nur die Zeile zu drucken:

awk '{print $1}' file | sort | uniq -c | 
while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 2-

Erläuterung:

Das awkSkript druckt nur das erste durch Leerzeichen getrennte Feld der Datei. Verwenden Sie $Ndiese Option, um das N-te Feld zu drucken. sortsortiert es und uniq -czählt die Vorkommen jeder Zeile.

Dies wird dann an die whileSchleife übergeben, die die Anzahl der Vorkommen als $numund die Zeile speichert, $dupewenn und wenn sie $numgrößer als eins ist (also mindestens einmal dupliziert wird), durchsucht sie die Datei nach dieser Zeile unter Verwendung von-n druckt die Zeilennummer. Das --sagt, grepdass das Folgende keine Befehlszeilenoption ist, die nützlich ist, wenn Sie $dupedamit beginnen können -.


1

Zweifellos könnte die ausführlichste in der Liste kürzer sein:

#!/usr/bin/python3
import collections
file = "file.txt"

def find_duplicates(file):
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    splitlines = [
        (index, data[index].split("  ")) for index in range(0, len(data))
        ]
    lineheaders = [item[1][0] for item in splitlines]
    dups = [x for x, y in collections.Counter(lineheaders).items() if y > 1]
    dupsdata = []
    for item in dups:
        occurrences = [
            splitlines_item[0] for splitlines_item in splitlines\
                       if splitlines_item[1][0] == item
            ]
        corresponding_lines = [
            "["+str(index)+"] "+data[index] for index in occurrences
            ]
        dupsdata.append((occurrences, corresponding_lines))

    # printing output   
    print("found duplicates:\n"+"-"*17)
    for index in range(0, len(dups)):
        print(dups[index], dupsdata[index][0])
        lines = [item for item in dupsdata[index][1]]
        for line in lines:
            print(line, end = "")


find_duplicates(file)

gibt auf eine Textdatei wie:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

eine Ausgabe wie:

found duplicates:
-----------------
dog [1, 4]
[1] dog  bone
[4] dog  cowmeat
monkey [0, 2]
[0] monkey  banana
[2] monkey  banana peanut

Sobald Sie die zu entfernenden Zeilen ausgewählt haben:

removelist = [2,1]

def remove_duplicates(file, removelist):
    removelist = sorted(removelist, reverse=True)
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    for index in removelist:
        data.pop(index)
    with open(file, "wt") as sourcefile:
        for line in data:
            sourcefile.write(line)

remove_duplicates(file, removelist)

0

Siehe folgende sortiert file.txt:

addons.mozilla.org/en-US/firefox/addon/click-to-play-per-element/ ::: C2P per-element
addons.mozilla.org/en-us/firefox/addon/prospector-oneLiner/ ::: OneLiner
askubuntu.com/q/21033 ::: What is the difference between gksudo and gksu?
askubuntu.com/q/21148 ::: openoffice calc sheet tabs (also askubuntu.com/q/138623)
askubuntu.com/q/50540 ::: What is Ubuntu's Definition of a "Registered Application"?
askubuntu.com/q/53762 ::: How to use lm-sensors?
askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors
stackoverflow.com/q/4594319 ::: bash - shell replace cr\lf by comma
stackoverflow.com/q/4594319 ::: shell replace cr\lf by comma
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence - Ubuntu Wiki
www.youtube.com/watch?v=1olY5Qzmbk8 ::: Create new mime types in Ubuntu
www.youtube.com/watch?v=2hu9JrdSXB8 ::: Change mouse cursor
www.youtube.com/watch?v=Yxfa2fXJ1Wc ::: Mouse cursor size

Da die Liste kurz ist, kann ich (nach dem Sortieren) sehen, dass es drei Sätze von Duplikaten gibt.

Dann kann ich zum Beispiel wählen:

askubuntu.com/q/53762 ::: How to use lm-sensors?

eher, als

askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors

Für eine längere Liste wird dies jedoch schwierig sein. Basierend auf den beiden Antworten, die eine vorschlagen uniqund die andere vorschlagen cut, stelle ich fest, dass dieser Befehl mir die Ausgabe gibt, die ich möchte:

$ cut -d " " -f1 file.txt | uniq -d
askubuntu.com/q/53762
stackoverflow.com/q/4594319
wiki.ubuntu.com/ClipboardPersistence
$

Ich habe meine Antwort mit einer anderen Variante von aktualisiert cut. Wenn Sie Deduplizierungsarbeiten ausführen, können Zeilennummern sehr hilfreich sein. Verwenden Sie zum Drucken aller Duplikate die -DOption anstelle von -d.
Lekensteyn

Ich denke, Sie verwenden besser: for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n $dup file.txt; donewie in meiner Antwort. Sie erhalten eine bessere Vorschau auf das, was Sie interessiert.
Radu Rădeanu

0

So habe ich es gelöst:

file_with_duplicates:

1,a,c
2,a,d
3,a,e <--duplicate
4,a,t
5,b,k <--duplicate
6,b,l
7,b,s
8,b,j
1,b,l
3,a,d <--duplicate
5,b,l <--duplicate

Datei sortiert und dedupiert nach Spalten 1 und 2:

sort -t',' -k1,1 -k2,2 -u file_with_duplicates

Datei nur nach Spalten 1 und 2 sortiert:

sort -t',' -k1,1 -k2,2 file_with_duplicates

Zeigen Sie nur den Unterschied:

diff <(sort -t',' -k1,1 -k2,2 -u file_with_duplicates) <(sort -t',' -k1,1 -k2,2 file_with_duplicates)

 3a4
   3,a,d
 6a8
   5,b,l
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.