2010-03-24 4 views
51

Ich habe zwei Dateien A - nodes_to_delete und B - nodes_to_keep. Jede Datei hat mehrere Zeilen mit numerischen IDs.bash, Linux: Unterschied zwischen zwei Textdateien einstellen

Ich möchte die Liste der numerischen IDs haben, die in nodes_to_delete sind, aber nicht in nodes_to_keep, z. alt text http://mathworld.wolfram.com/images/equations/SetDifference/Inline1.gif.

In einer PostgreSQL-Datenbank zu tun ist unangemessen langsam. Irgendein einfacher Weg, es in bash mit Linux CLI-Tools zu tun?

UPDATE: Dies scheint ein Pythonic Job zu sein, aber die Dateien sind wirklich, wirklich groß. Ich habe einige ähnliche Probleme unter Verwendung von uniq, sort und einigen Mengenlehretechniken gelöst. Dies war etwa zwei oder drei Größenordnungen schneller als die Datenbankäquivalente.

+0

Ich bin neugierig, was Antworten kommen werden. Bash ist ein bisschen mehr segphault, Systemadministrator glaube ich. Wenn du "in python" oder "in php" gesagt hättest oder was auch immer deine Chancen gewesen wären :) – extraneon

+0

Ich sah den Titel und war bereit, UI-Inkonsistenzen und heiliger-als-du-Hilfe-Foren zu bahsen. Das hat mich enttäuscht, als ich die eigentliche Frage gelesen habe. :( – aehiilrs

Antwort

83

Der Befehl comm macht das.

+9

Und wenn die Dateien noch nicht sortiert sind, 'sort' zuerst. – extraneon

+2

+1 Erleuchtete, tolles Werkzeug, dass ich mich dumm fühlen, nicht gewusst zu haben. Danke! –

+5

@Adam Matan: viel mehr Aufklärung verfügbar bei' ls/bin/usr/bin | xargs man' –

1

Vielleicht brauchen Sie einen besseren Weg, um es in Postgres tun, kann ich ziemlich wetten, dass Sie einen schnelleren Weg finden, um es mit flachen Dateien zu tun. Sie sollten in der Lage sein, einen einfachen inneren Join zu machen und davon auszugehen, dass beide ID-Spalten indexiert sind, was sehr schnell sein sollte.

+0

Sie sind technisch korrekt, und das' explain' unterstützt Ihren Anspruch, aber es funktioniert einfach nicht für sehr große (~ zig Millionen) Tabellen. –

+1

Ja, es würde durch deine Erinnerung eingeschränkt werden, im Gegensatz zu etwas wie einer sortierten Kommunikation, aber ich würde denken, dass wenn du zwei Tabellen mit nur einem int-ID-Feld hast, du ohne Probleme in die 10er Millionen kommen könntest. –

+0

Das ist in der Theorie richtig, aber es funktioniert einfach nicht aus irgendeinem Grund. –

26

Jemand hat mir vor ein paar Monaten gezeigt, wie man genau das macht, und dann konnte ich es eine Zeit lang nicht finden ... und beim Blick fiel ich auf deine Frage. Hier ist sie:

set_union() { 
    sort $1 $2 | uniq 
} 

set_difference() { 
    sort $1 $2 $2 | uniq -u 
} 

set_symmetric_difference() { 
    sort $1 $2 | uniq -u 
} 
+1

Ich denke, das ist besser als die akzeptierte Antwort ... "Comm" ist nicht in allen Umgebungen verfügbar. – danwyand

+0

Und 'comm' funktioniert auch nicht auf' stdin' – wieczorek1990

+3

Das ist symmetrische Differenz, nicht normal eingestellte Differenz. – Tgr

1

Verwendung comm - es zwei sortierte Dateien Zeile für Zeile vergleichen wird

Die Antwort auf OP Frage mit diesem Beispiel-Setup unten erscheint. Dieser Befehl Linien eindeutig deleteNodes zurückkehren, nicht in keepNodes

comm -1 -3 <(sort keepNodes) <(sort deleteNodes) 

Erklärung: anzeigen Linien eindeutig deleteNodes, verbergen andere Linien


Beispiel Setup

Wir verwenden keepNodes und deleteNodes. Sie werden als unsortierte Eingabe verwendet.

$ cat > keepNodes <(echo bob; echo amber;) 
$ cat > deleteNodes <(echo bob; echo ann;) 

standardmäßig ohne Argumente, comm druckt 3 Spalten

unique_to_FILE1 
    unique_to_FILE2 
     lines_appear_in_both 

Dies ist ein Barebone Beispiel comm ohne Argumente. Beachten Sie die drei Spalten.

$ comm <(sort keepNodes) <(sort deleteNodes) 
amber 
    ann 
     bob 

Unterdrückspaltenausgangs

Suppress Spalte 1, 2 oder 3 mit -N; Beachten Sie, dass beim Verdecken einer Spalte der Leerraum schrumpft.

$ comm -1 <(sort keepNodes) <(sort deleteNodes) 
ann 
    bob 
$ comm -2 <(sort keepNodes) <(sort deleteNodes) 
amber 
    bob 
$ comm -3 <(sort keepNodes) <(sort deleteNodes) 
amber 
    ann 
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes) 
ann 
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes) 
amber 
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes) 
bob 

Es wird anmutig fehlschlagen, wenn Sie zu sortieren vergessen

comm: file 1 is not in sorted order

+0

+1 für korrekte Beispiele, die die Antwort auf die spezifische Frage des OP enthalten (Ausgabezeilen in 'deleteNodes', die nicht in' keepNodes' sind), aber es wäre besser, wenn die richtige Lösung hervorgehoben wäre: 'comm -1 -3 <(sort keepNodes) <(sort deleteNodes) '. –

1

comm speziell für diese Art von Anwendungsfall entwickelt wurde, aber es erfordert sortierten Eingang.

awk ist wohl ein besseres Werkzeug für diese, da es ziemlich geradlinig ist, Set-Differenz zu finden, erfordert sort nicht, und bietet zusätzliche Flexibilität.

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete 

Vielleicht, zum Beispiel, würden Sie nur gerne den Unterschied in Linien finden, das nicht-negative Zahlen darstellen:

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r { 
    a[$0] 
    next 
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete