Gibt es ein Tool, um die Zeilen in einer Datei abzurufen, die sich nicht in einer anderen befinden?

Antworten:

159

Ja. Das Standardwerkzeug grepzum Durchsuchen von Dateien nach Textzeichenfolgen kann verwendet werden, um alle Zeilen in einer Datei von einer anderen zu subtrahieren.

grep -F -x -v -f fileB fileA

Dies funktioniert, indem jede Zeile in DateiB als Muster ( -f fileB) und als einfache Zeichenfolge (nicht als regulärer regulärer Ausdruck) ( -F) behandelt wird. Sie erzwingen die Übereinstimmung in der gesamten Zeile ( -x) und drucken nur die Zeilen aus, die nicht übereinstimmen ( -v). Aus diesem Grund drucken Sie die Zeilen in Datei A aus, die nicht dieselben Daten wie die Zeilen in Datei B enthalten.

Der Nachteil dieser Lösung ist, dass die Zeilenreihenfolge nicht berücksichtigt wird. Wenn Ihre Eingabe doppelte Zeilen an verschiedenen Stellen enthält, erhalten Sie möglicherweise nicht das, was Sie erwarten. Die Lösung hierfür ist die Verwendung eines echten Vergleichstools wie z diff. Sie könnten dies tun, indem Sie eine Diff-Datei mit dem Kontextwert in 100% der Zeilen in der Datei erstellen und dann nur die Zeilen analysieren, die beim Konvertieren von Datei A in Datei B entfernt würden. (Beachten Sie, dass dieser Befehl auch das Diff entfernt Formatierung, nachdem es die richtigen Linien bekommt.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
quelle
@ inderpreet99 Das -uArgument in Kleinbuchstaben nimmt tatsächlich einen Parameter einer Zahl an, solange auf ihn kein Leerzeichen folgt. Der Vorteil, den ich vorher hatte, ist, dass es mit oder ohne Wert funktioniert, sodass Sie in dieser Unterbefehlsroutine etwas verwenden können, das keine Ausgabe zurückgibt. Großbuchstaben '-U' erfordern dagegen ein Argument.
Caleb
Seien
Sie
1
Die diffPipeline funktioniert ein Vergnügen, danke.
Felipe Alvarez
Um das Sortierproblem zu berücksichtigen, können Sie die Prozessersetzung im Befehl verwenden, um jede Datei vor der greperforderlichen zu verarbeiten. Beispiel:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro
@TonyCesaro Das würde funktionieren, wenn Ihr Datensatz nicht auftragsspezifisch ist und Duplikate nicht berücksichtigt werden müssen. Der Vorteil der Verwendung diffist, dass die Position in der Datei berücksichtigt wird.
Caleb
57

Die Antwort hängt stark von der Art und dem Format der zu vergleichenden Dateien ab.

Wenn die Dateien, die Sie vergleichen, sortierte Textdateien sind, kann das von Richard Stallman und Davide McKenzie aufgerufene GNU-Tool die von commIhnen gewünschte Filterung durchführen. Es ist Teil der Coreutils.

Beispiel

Angenommen, Sie haben die folgenden 2 Dateien:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Zeilen in der Datei b, die nicht in der Datei enthalten sind a:

$ comm <(sort a) <(sort b) -3
    6
Ein Freund
quelle
1
+1 für Erwähnung comm; Leider commerfordert sortierte Dateien
Arcege
11
also sortiere sie? komm <(sortiere a) <(sortiere b) -1 -2
Sirex
Dies ist eine seltsame Syntax. <()? Es funktioniert und ich verstehe, aber gibt es einen Namen für diese Verrücktheit?
mlissner
2
@mlissner <()wird auch als Prozesssubstitution bezeichnet .
Miku
1
commwurde ursprünglich um 1973 von jemandem bei Bell Labs geschrieben, nicht von rms. Sie beziehen sich auf die GNU-Implementierung, die viel später erfolgte. Im Laufe der Jahre gab es viele verschiedene Implementierungen der Unix-Dienstprogramme.
Stéphane Chazelas
32

von stackoverflow ...

komm -23 datei1 datei2

-23 unterdrückt die Zeilen, die sich in beiden Dateien oder nur in Datei 2 befinden. Die Dateien müssen sortiert werden (sie sind in Ihrem Beispiel), aber wenn nicht, leiten Sie sie zuerst durch sort ...

Siehe die Manpage hier

JJS
quelle
Dies gilt nicht für mich arbeiten, aus irgendeinem Grunde ...
Jan
@Jan Sind deine Dateien sortiert? Wie hast du sie sortiert?
JJS
8

Die Methoden grep und comm (with sort) nehmen bei großen Dateien viel Zeit in Anspruch. SiegeX und ghostdog74 haben zwei großartige awk-Methoden zum Extrahieren von Zeilen verwendet, die nur für eine von zwei Dateien im Stapelüberlauf gelten:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
quelle
2
Wenn Sie dies mit großen Dateien tun, sind die Speicherbeschränkungen beim Laden einer großen Datei in ein assoziatives Array unerschwinglich.
Charles Duffy
4

Wenn die Dateien groß sind und Sie keine benutzerdefinierte Reihenfolge für Ihre Einträge haben, dauert grep viel zu lange. Eine schnelle Alternative wäre

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[file2-file1 führt zum Screen, Pipe zur Datei usw.]

Ändern >zu <würde die entgegengesetzte Subtraktion erhalten.rm 1 2

Eshel Faraggi
quelle
2

Sie können auch vimdiff in Betracht ziehen, um die Unterschiede zwischen Dateien in einem vim-Editor hervorzuheben

simona
quelle
1
Aber gibt es eine einfache Möglichkeit, die Subtraktion in Vimdiff automatisch durchzuführen?
Kazark