Suchen Sie IDs in einer Datei, die sich nicht in einer anderen befinden


9

Ich habe zwei Dateien:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Ich möchte überprüfen, ob "abcd" in der Datei mno.txt vorhanden ist .
  • Es ist nicht erforderlich, dass "abcd", wenn es in abc.txt an erster Stelle steht , auch zuerst in mno.txt angezeigt wird .
  • In beiden Dateien befinden sich Tausende solcher IDs.
  • Ich möchte auch überprüfen, wie viele IDs nicht in mno.txt sind, die in abc.txt sind .

Wie kann ich das machen ?

Antworten:


19

Wenn Ihr Ziel darin besteht, gemeinsame oder ungewöhnliche Linien zu finden, commwäre dies mein Befehl hier.

Es vergleicht zwei Dateien und zeigt in drei Spalten Zeilen an, die für Datei 1 eindeutig sind, Zeilen, die für Datei 2 eindeutig sind, und Zeilen, die in beiden Dateien angezeigt werden. Sie können es-Flags übergeben, um auch diese Ausgabe zu unterdrücken. ZB comm -1 file1 file2wird die erste Spalte unterdrückt, die Dinge, die nur für Datei1 gelten. comm -12 file1 file2würde nur Dinge in beiden Dateien zeigen.

Es gibt eine große Einschränkung: Die Eingabe muss sortiert werden. Wir können das umgehen.

Dies zeigt Ihnen alles in abc, was nicht in mno ist:

comm -23 <(sort abc.txt) <(sort mno.txt)

Und Sie können das einpfeifen, wc -lum eine Zählung zu erhalten.


Der Grund, warum ich mich dafür entscheide, commist, dass der Vergleich nebeneinander nach dem Sortieren der Dateien rechnerisch sehr einfach ist. Wenn Sie mit Millionen davon zu tun haben, wird das einen Unterschied machen.

Dies kann mit einigen Scheindateien demonstriert werden. Ich habe einen ziemlich schnellen Computer, um den Unterschied zwischen den Ansätzen zu zeigen, brauche ich ein ziemlich großes Probenset. Ich habe 10 Millionen 10-Zeichen-Zeichenfolgen pro Datei verwendet.

$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > abc.txt
$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > mno.txt

$ time comm -23 <(sort abc.txt) <(sort mno.txt) | wc -l
... 0m10.653s

$ time grep -Fcxv -f abc.txt mno.txt
... 0m23.920s

$ time grep -Fcwv -f abc.txt mno.txt
... 0m40.313s

$ time awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt | wc -l
... 0m12.161s

Das Sortieren dauert bei mir die meiste Zeit. Wenn wir so tun, als wäre abc.txt statisch, können wir es vorsortieren, was zukünftige Vergleiche viel schneller macht:

$ sort abc.txt abc-sorted.txt
$ time comm -23 abc-sorted.txt <(sort mno.txt) | wc -l
... 0m7.426s

Sie könnten sich diese ansehen und einige Sekunden für irrelevant halten, aber ich muss hervorheben, dass diese auf einem High-End-Computer ausgeführt werden. Wenn Sie dies auf einem (z. B.) Raspberry Pi 3 tun möchten, werden Sie viel langsamere Turnarounds sehen und der Unterschied wird bis zu einem Punkt zunehmen, der tatsächlich wichtig ist.


7

um eine Liste zu bekommen:

grep -Fwf abc.txt mno.txt

es gibt Ihnen etwas ähnliches wie:

abcd
abcd
zef

Wenn Sie nur eine eindeutige Liste erhalten möchten, verwenden Sie diese wie folgt:

grep -Fwf abc.txt mno.txt | sort | uniq

und um die Zählungen zu bekommen:

grep -Fcwv -f abc.txt mno.txt

  • -F bedeutet: PATTERN als Liste fester Zeichenfolgen anstelle regulärer Ausdrücke interpretieren.
  • -fErhalten Sie Muster aus der DATEI, die sein werden abc.txt.
  • Wir suchen nach mno.txtMustern
  • -c Zählen Sie die Anzahl der Übereinstimmungen
  • -wSuchen Sie nur nach "ganzen Wörtern": Der übereinstimmende Teilstring muss entweder am Anfang der Zeile stehen oder vor einem Nicht-Wort-Bestandteil stehen. Ebenso muss es entweder am Ende der Zeile stehen oder von einem Nicht-Wort-Bestandteil gefolgt werden. Wortbestandteilszeichen sind Buchstaben, Ziffern und der Unterstrich.
  • -v Stornieren Sie die Suche

1
Wenn das OP eine Anzahl von Nicht- Übereinstimmungen wünscht , sollte das nicht eher so sein grep -cxvFf abc.txt mno.txt?
Steeldriver

Habe

Zu Ihrer Information der fgrep, egrepist wechselt angeblich veraltet (für grep -F, grep -E- obwohl ich jemand bin nicht sicher , glaubt sie immer weggeht
steeldriver

Ist es notwendig, -xbei der Verwendung zu verwenden -F?
Ravexina

1
Es kommt darauf an, was das OP genau zählen möchte - zB wenn mno.txt enthält, abcdefsollte das als Übereinstimmung oder Nichtübereinstimmung gelten abcd?
Steeldriver

3

Wir könnten awk verwenden, um die Arbeit zu erledigen, indem wir zwei Dateien übergeben, zuerst die Musterdatei, dann die Datei, die wir überprüfen möchten. Wenn wir die erste Datei lesen, wissen wir das NR==FNRund können zu diesem Zeitpunkt Zeilen in ein Array einlesen. Wenn NR!=FNRwir prüfen, ob das Array für eine solche Zeile festgelegt ist.

$ cat abc.txt                                                      
abcd
xyz
pqrs
$ cat mno.txt                                                      
zzon
xyz
mkno
abcd
$ awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt         
xyz
abcd

Umgekehrt können wir das Muster negieren, um die Zeilen zu drucken, die nicht vorhanden sind abc.txt

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt       
zzon
mkno

Und wenn wir die Anzahl derer ausdrucken möchten, die wir beschäftigen können, sortund wc:

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2

Ich denke du hast es falsch herum. Soweit ich verstehe die Frage, OP will die (Größe) , um die eingestellte Differenz von berechnen abc.txt- mno.txtdas ist {xyz, pqrs}.
David Foerster

2

Wenn eine der Wortlisten unsortiert ist, ist es schneller, eine effiziente Satzdatenstruktur zu verwenden, um sich an die gebräuchlichen Wörter zu erinnern.

Python

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = frozenset(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = frozenset(map(str.rstrip, subtrahend_file))

difference = minuend - subtrahend
#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Verwendungszweck:

python3 set-difference.py abc.txt mno.txt

Python (effizienter)

Wenn Sie ein wenig Speicher für Zwischenspeicher und Laufzeit sparen möchten, können Sie dieses etwas schwieriger zu verstehende Programm verwenden:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = set(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = map(str.rstrip, subtrahend_file)
    minuend.difference_update(subtrahend)
    difference = minuend
    del minuend

#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Performance

Gegeben abc.txtund mno.txtmit 1 Million unsortierten Zeilen mit jeweils 10 zufälligen ASCII-Ziffern (siehe Olis Antwort für die Einrichtung):

$ time python3 set-difference.py abc.txt mno.txt
user    0m10.453s

vs.

$ export LC_COLLATE=C
$ time sort abc.txt > abc_sorted.txt
user    0m10.652s
$ time sort mno.txt > mno_sorted.txt
user    0m10.767s
$ time comm -23 abc_sorted.txt mno_sorted.txt | wc -l
9989882
user    0m1.600s

Gesamt: 23 Sekunden

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.