Was ist der einfachste Weg, um Zeilen aus einer Datei zu entfernen, die mit Zeilen aus einer anderen Datei übereinstimmen?

4

Was ist der einfachste Weg, um Zeilen aus einer Datei zu entfernen, die mit Zeilen aus einer anderen Datei übereinstimmen? Zum Beispiel, wenn ich die folgenden Dateien habe:

file1.csv:

u2@domain.com

file2.csv:

1,u1@domain.com,somehash1
2,u2@domain.com,somehash2
3,u3@domain.com,somehash3

Als Ergebnis hätte ich gerne file3.csv :

1,u1@domain.com,somehash1
3,u3@domain.com,somehash3

Was ist der schnellste Weg, um diese Aufgabe zu lösen? Diese Dateien sind einige GB groß.

AB
quelle
2
Scheint zu groß für alles andere als eine Codierungslösung ehrlich ...
soandos
2
Haben Sie genug RAM, um alle Dateien gleichzeitig im RAM zu speichern + 2 Gigs? Wenn nicht, benötigen Sie Code, da kein Programm sie gleichzeitig geöffnet halten kann.
Soandos
Das ist ein guter Punkt. Während meine Lösung theoretisch funktioniert, wird das Gedächtnis wahrscheinlich ein begrenzender Faktor sein. Vielleicht können Sie die Dateien zuerst aufteilen?
Cam Jackson

Antworten:

7

grep -v -F -f file1.csv file2.csv > file3.csvscheint das einfachste zu sein. Sie sollten jedoch zuerst Leistungstests mit kleineren Dateien durchführen. (Ich stimme dem Kommentar von soandos zu, dass solch große Dateien möglicherweise eine dedizierte Lösung benötigen.)

Idioten
quelle
So wurde es gelöst:cat file2.csv | fgrep -vf file1.csv > file3.csv
AB
2
Anscheinend ist das, was Sie verwendet haben, praktisch die gleiche Methode, obwohl dies ein Beispiel für die nutzlose Verwendung von darstelltcat . Sie könnten auch verwenden < file2.csv | fgrep -vf file1.csv > file3.csv.
jankes
1
Ich denke du brauchst auch die -xOption.
Akostadinov
3
awk -F, '
  FILENAME == ARGV[1] {to_remove[$1]=1; next}
  ! ($2 in to_remove) {print}
' file1.csv file2.csv > file3.csv

Sie müssen über genügend Speicher verfügen, um Datei1 auf einmal einlesen zu können.

Hier ist eine andere Option: join

$ join -t , -v 2 -1 1 -2 2 file1.csv file2.csv
u1@domain.com,1,somehash1
u3@domain.com,3,somehash3

Aus der Manpage "Wichtig: DATEI1 und DATEI2 müssen in den Verknüpfungsfeldern sortiert sein." Berücksichtigen Sie dies bei Ihrer Entscheidung.

Glenn Jackman
quelle
1

Sie könnten jede Zeile in Datei1 durchlaufen und passende Zeilen aus Datei2 abrufen?

cp file2.csv file3.csv
cat file1.csv | while read line; do
    grep -v ${line%?} file3.csv > temp.csv
    cat temp.csv > file3.csv
done
rm -f temp.csv

Ungetestet.

Edit: Getestet, scheint OK zu funktionieren. Stellen Sie einfach sicher, dass in Datei1 ein Zeilenumbruch vorhanden ist.

Cam Jackson
quelle
Die Variable 'line' wird nicht mit einem nachgestellten Zeilenumbruch versehen, sodass Sie das letzte Zeichen nicht abhacken müssen. Dies kann zu Fehlalarmen führen. 'cat temp.csv> file3.csv' wird effizienter als 'mv temp.csv file3.csv' geschrieben. Vor allem aber verarbeiten (lesen UND schreiben) Sie die größere Datei2 mehrmals (einmal für jede Zeile in Datei1) - es gibt Ansätze, die Datei2 nur einmal durchlaufen.
Glenn Jackman
Hmm, als ich es getestet habe, hat es anfangs nicht funktioniert. Ich fügte hinzu set -xund set -vund sah, dass eine neue Zeile am Ende des Strings war, nach dem gesucht wurde, also fügte ich die hinzu %?und es funktionierte ... Und Sie haben Recht, dass dies Datei2 einmal für jede Zeile in Datei1 verarbeitet, aber sicherlich die einzige Ein anderer Ansatz wäre, file1 einmal für jede Zeile in file2 zu lesen. Das OP sagte, es handele sich bei den Dateien um "jeweils wenige Konzerte", daher bin ich mir nicht sicher, wie groß der Unterschied zwischen den Dateigrößen ist.
Cam Jackson
1

Muss file1.csv unverändert bleiben?

sed 's|.*|/^&.*/d|' file1.csv > file1.sed
sed -f file1.sed file2.csv > file3.csv 

Ich weiß nicht, wie viel Speicher es verbraucht. AFAIK, es testet jedes Mal die Datei mit dem gesamten Wert (2.csv).

Wenn die Eingabe sortiert ist und auch die Muster sortiert sind, können Sie eine schnellere Lösung implementieren.

Benutzer unbekannt
quelle
0

Stellen Sie sicher, dass file3.csv existiert (und leer ist)

echo > file3.csv
diff file1.csv file2.csv | patch file3.csv

Et voilá!

Großrechner
quelle
Die Zeilen, die aus Datei2 entfernt werden, stimmen nicht genau mit den Zeilen aus Datei1 überein. Das Beispiel in der Frage zeigt, dass Zeilen aus Datei1 eine Teilmenge einer Zeile aus Datei2 sind. Das wird also nicht funktionieren, oder?
Cam Jackson
Richtig, es wird nicht funktionieren.
Mainframezen