Unterschied von zwei großen Dateien

14

Ich habe "test1.csv" und es enthält

200,400,600,800
100,300,500,700
50,25,125,310

und test2.csv und es enthält

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

jetzt

diff test2.csv test1.csv > result.csv

ist anders als

diff test1.csv test2.csv > result.csv

Ich weiß nicht, welche Reihenfolge richtig ist, aber ich möchte etwas anderes. Die beiden obigen Befehle geben etwa Folgendes aus

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Ich möchte nur den Unterschied ausgeben, daher sollte results.csv so aussehen

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Ich habe versucht , diff -qund , diff -saber sie haben es nicht getan. Ordnung spielt keine Rolle, wichtig ist, dass ich nur den Unterschied sehen möchte, weder> noch <noch Leerzeichen.

grep -FvF habe den Trick bei kleineren Dateien gemacht, nicht bei großen

Die erste Datei enthält mehr als 5 Millionen Zeilen, die zweite Datei enthält 1300 Zeilen.

Die Datei results.csv sollte also ~ 4.998.700 Zeilen ergeben

Ich habe auch versucht, grep -F -x -v -f was nicht funktioniert hat.

Lynob
quelle
1
@Tim Ich habe Ihren Link gesehen und bin ein altes Mitglied, daher kenne ich die Regeln, war aber nachlässig. Tut mir leid. Ich habe ihn bearbeitet und in einem Popup-Fenster wurde der Beitrag bearbeitet, sodass Sie die Arbeit für mich erledigten und ich dankbarer Herr.
Lynob
50,25,125,310ist für beide Dateien gleich .. Sie müssen das von Ihrer gewünschten Ausgabe entfernen ..
heemayl
Soll die Bestellung erhalten bleiben?
Kos
1
Art hängt davon ab, was Sie mit den Informationen tun möchten, diff, IMO, ist für die Erstellung eines Patches. Auf jeden Fall bin ich mir jetzt sicher, ob Sie das beste Tool haben, diff, grep, awk oder perl.
Panther

Antworten:

20

Klingt nach einem Job für comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Wie erklärt in man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Das -3bedeutet also, dass nur Zeilen gedruckt werden, die für eine der Dateien eindeutig sind. Diese werden jedoch entsprechend der Datei eingerückt, in der sie gefunden wurden. Verwenden Sie zum Entfernen der Registerkarte Folgendes:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

In diesem Fall müssen Sie die Dateien nicht wirklich sortieren, und Sie können das oben Gesagte vereinfachen, um:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
terdon
quelle
Sie haben sich nicht von den Leerzeichen nach der 200,[...]Zeile täuschen lassen, oder ? :)
Kos
@kos nein, ich habe zuerst die nachgestellten Leerzeichen aus den Dateien entfernt. Ich ging davon aus, dass die OP-Dateien sie nicht wirklich haben.
Terdon
6

Verwendung grepmit bashProzessersetzung:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

So speichern Sie die Ausgabe als results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()ist das bashProzesssubstitutionsmuster

  • grep -vFf test2.csv test1.csv finde die Zeilen nur einmalig test1.csv

  • grep -vFf test1.csv test2.csv finde die Zeilen nur einmalig test2.csv

  • Schließlich fassen wir die Ergebnisse von zusammen cat

Oder wie von Oli vorgeschlagen , können Sie auch die Befehlsgruppierung verwenden:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Oder laufen Sie einfach nacheinander, während beide an STDOUT schreiben. Letztendlich werden sie hinzugefügt:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
heemayl
quelle
1
Warum catzwei umgeleitete Befehle? Warum nicht einfach eins dann das andere laufen lassen? grep ... ; grep ...oder { grep ... ; grep ... ; }wenn du etwas mit dem kollektiven Output anfangen wolltest.
Oli
@Oli Danke .. das ist eine großartige Idee .. ich habe nicht daran gedacht ..
heemayl
4

Wenn die Reihenfolge der Zeilen nicht relevant ist, verwenden Sie awk oder perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Verwenden Sie grep, um die gemeinsamen Zeilen abzurufen und diese herauszufiltern:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

Das interne grep erhält die gemeinsamen Zeilen, und das externe grep findet Zeilen, die nicht mit diesen gemeinsamen Zeilen übereinstimmen.

muru
quelle
Ihr awk-Befehl wird nur erneut implementiert sort | uniq -u, was zu einer falschen Antwort führt, wenn eine Datei doppelte Zeilen enthält. Für grep würde ich "inner" / "äußer" sagen, nicht "inner" / "äußer".
Peter Cordes
@PeterCordes ja, das tut es und von wem soll man sagen, dass das das falsche Ergebnis ist?
Muru
Falsch in dem Sinne, dass es nicht genau das ist, was die Frage in diesem Eckfall verlangt. Es mag sein, was jemand will, aber Sie sollten den Unterschied zwischen dem, was Sie awkdrucken, und dem, was die comm -3und die diffAntworten drucken, hervorheben.
Peter Cordes
@PeterCordes nochmal, wer bist du, um das zu sagen? Bis das OP sagt, dass es das ist, was sie wollen, ist es mir egal, ob sich die Ausgabe von unterscheidet comm -3. Ich sehe keinen Grund, warum ich das erklären sollte. Wenn Sie eine Notiz bearbeiten möchten, zögern Sie nicht.
Muru
Der OP sagte, er wolle den Unterschied. Das ist nicht immer das, was Ihr Programm produziert. Ein Programm, das die gleiche Ausgabe für einen Testfall erzeugt, aber nicht die Beschreibung für alle Fälle erfüllt, erfordert ein Heads-up. Ich bin hier, um das zu sagen, und es ist wahr, egal wer ich bin oder wer du bist. Ich habe eine Notiz hinzugefügt.
Peter Cordes
4

Verwenden Sie die --*-line-format=...Optionen vondiff

Sie können diffgenau sagen , was Sie brauchen - unten erklärt:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

Es ist möglich, die Ausgabe von diff sehr detailliert festzulegen, ähnlich wie bei a printf Zahlenformat.

Die Zeilen aus der ersten Datei test1.csvheißen "alte" Zeilen, und die Zeilen aus der zweiten,test2.csv sind "neue" Zeilen. Das macht Sinn, wenn diffman sieht, was sich in einer Datei geändert hat.

Die Optionen, die wir benötigen, sind diejenigen, die das Format für "alte" Zeilen, "neue" Zeilen und "unveränderte" Zeilen festlegen.
Die Formate, die wir benötigen, sind sehr einfach:
Für die geänderten Zeilen, neue und alte, möchten wir nur den Text der Zeilen ausgeben. %List das Formatsymbol für den Zeilentext.
Für die unveränderten Zeilen wollen wir nichts anzeigen.

Hiermit können wir Optionen wie schreiben --old-line-format='%L'und alles anhand Ihrer Beispieldaten zusammenfassen:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Hinweise zur Leistung

Da die Dateien eine unterschiedliche Größe haben, versuchen Sie, die Eingabedateien auszutauschen, wenn es keine Rolle spielt, es könnte sein, dass das Innenleben von diff besser mit der einen als mit der anderen umgehen kann. Besser ist es, entweder weniger Speicher oder weniger Berechnungen zu benötigen.

Es gibt eine Optimierungsoption für die Verwendung diffmit großen Dateien: --speed-large-files. Es werden Annahmen zur Dateistruktur verwendet, daher ist nicht klar, ob dies in Ihrem Fall hilfreich ist, aber es lohnt sich, es zu versuchen.

Die Formatoptionen sind in der beschrieben man diffunter --LTYPE-line-format=LFMT.

Volker Siegel
quelle
3

Da die Bestellung nicht aufbewahrt werden muss, einfach:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: verschmilzt und sortiert test1.csvundtest2.csv
  • uniq -u: druckt nur die Zeilen, die kein Duplikat haben
kos
quelle
Das funktioniert nicht, wenn eine Datei zweimal eine Zeile enthält, die in der anderen Datei nicht vorkommt. Beide Vorkommen würden zu einem diffErgebnis führen.
Volker Siegel