Überlappen / Vergleichen von zwei Dateien und Drucken von nicht übereinstimmenden Dateien

7

Hallo, ich habe zwei Dateien mit einigen Dateinamen, die so aussehen:

Datei 1:

123.txt
456.txt
789.txt
101112.txt

Datei 2:

123.txt 
789.txt
101112.txt

Gibt es einen Bash-Befehl, mit dem ich sie überlappen und nur die Zeilen oder Dateinamen drucken kann, die nicht übereinstimmen? Ich erwarte also so etwas:

456.txt
Mailand
quelle
... seltsame Erwartung, da die Zeilen 2,3 und 4 aus Datei 1 nicht mit denen aus Datei 2 übereinstimmen. Andernfalls hätten Sie sie verwenden können diff --suppress-common-lines.
Mike, was auch immer
Wie? Nachdem ich mich zum Beispiel file2damit überlappt habe file1 456.txt, möchte ich nicht mehr mithalten
Mailand
Ist diff --suppress-common-linessucht Differenz innerhalb jeder Zeile?
Mailand
Ja, das sollte es. Schauen Sie sich man difffür weitere Informationen.
Mike, was auch immer
1
Wenn Sie diese spezifische Ausgabe wünschen, lesen Sie die Antworten unten. Wenn Sie ein Allzweckwerkzeug wünschen, das Ihnen die Unterschiede zwischen zwei Dateien anzeigt (diese Zeile wurde hinzugefügt, diese Zeile wurde gelöscht, diese Zeilen wurden geändert ...), dann diffist dies das Standardwerkzeug dafür. Es gibt viele Optionen zur Steuerung des Verhaltens und verschiedene Ausgabeformate.
JCaron

Antworten:

13

comm ist dein Freund hier:

Wenn die Dateien bereits sortiert sind:

comm -3 f1.txt f2.txt

Wenn nicht sortiert, sortund übergeben Sie sie als Dateideskriptoren mithilfe der Prozessersetzung (damit wir keine temporären Dateien benötigen):

comm -3 <(sort f1.txt) <(sort f2.txt)

Beispiel:

% cat f1.txt
123.txt
456.txt
789.txt
101112.txt

% cat f2.txt
123.txt
789.txt
101112.txt

% comm -3 <(sort f1.txt) <(sort f2.txt)
456.txt
heemayl
quelle
3

Ein einfacher Ansatz wäre die Verwendung von zwei 'grep'-Befehlen, die jeweils eine der Dateien als Liste von Zeilen verwenden, um die andere Datei zu durchsuchen. Angenommen, Ihre Dateien heißen f1.txt und f2.txt:

grep -Fxvf f1.txt f2.txt ; grep -xvf f2.txt f1.txt

Die grepOptionen sind wie folgt:

  • -F - Verwenden Sie jede Zeile als feste Zeichenfolge und nicht als regulären Ausdruck
  • -x - Nur ganze Zeilen abgleichen
  • -v - Invertieren Sie die Übereinstimmung, um nicht übereinstimmende Linien auszuwählen
  • -f - Verwenden Sie die als Argument angegebene Datei als Liste der übereinstimmenden Muster
Arronisch
quelle
1
Gut, besser als meine Loops. Ich habe vergessen, dass grep auch Ausdrücke aus Dateien lesen kann. Sie sollten jedoch wahrscheinlich -F hinzufügen, um die Ausdrücke als feste Zeichenfolgen und nicht als Regex-Muster zu behandeln.
Byte Commander
Ich dachte, ich sollte das tun, war mir aber nicht sicher, ob -xdies in diesem Fall noch notwendig wäre.
Arronical
2

Ich verstehe Ihre Frage so, wie Sie alle Zeilen, die nur in einer der Dateien erscheinen, nicht in beiden, und ohne Berücksichtigung der Zeilenreihenfolge.

Ich gehe auch davon aus, dass wir die Dateien f1.txtund vergleichen f2.txt. Geben Sie stattdessen Ihre jeweiligen Namen ein.

Mit Bash können Sie dies mit zwei Schleifen tun, wobei jede eine Datei verarbeitet und für jede Zeile prüft, ob sie in der anderen erscheint. Dieser Ansatz ist nicht sehr effizient, sollte aber funktionieren:

# This loops over f1.txt and searches each line in f2.txt
while read line ; do grep -Fxqe "$line" f2.txt || echo "$line" ; done < f1.txt 

# This loops over f2.txt and searches each line in f1.txt
while read line ; do grep -Fxqe "$line" f1.txt || echo "$line" ; done < f2.txt 

Beide Schleifen zusammen ergeben das gewünschte Ergebnis. Jeder für sich sucht nur nach Zeilen in einer Datei, die in der anderen nicht erscheinen.

Eine sauberere Lösung könnte zB mit einem kurzen Python-Einzeiler geschrieben werden:

python3 -c 's1=set(open("f1.txt")); s2=set(open("f2.txt")); print(*s1.symmetric_difference(s2), sep="")'

Dies verwendet eine Set-Datenstruktur, die nur eindeutige Werte enthält und Set-Operationen wie "symmetrische Differenz" ermöglicht.

Beachten Sie, dass bei Verwendung beider Lösungen, wenn eine der Dateien doppelte Zeilen enthält, diese ignoriert und nur wie ein einziges Vorkommen behandelt werden.

Byte Commander
quelle
1

Angenommen, Sie benötigen die Ergebnisse nicht, um in der ursprünglichen Reihenfolge zu bleiben, verwenden Sie einfach:

cat file1 file2 | sort | uniq -u

Erläuterung:

cat file1 file2

Gibt beide Dateien nacheinander als Standardausgabe aus.

sort

Sortiert den kombinierten Inhalt der beiden Dateien. Der nützliche Nebeneffekt, an dem wir interessiert sind, besteht darin, dass identische Zeilen aus beiden Dateien direkt nebeneinander stehen.

uniq -u

Gibt nur die Zeilen aus, die "eindeutig" sind, dh nur einmal vorkommen. Ärgerlicherweise werden hier nur Paare benachbarter Zeilen betrachtet, weshalb der vorherige sortBefehl erforderlich ist.

Sie können auch uniq -dnur die Zeilen ausgeben, die zweimal vorkommen. Dadurch erhalten Sie die Zeilen, die beiden Dateien gemeinsam sind.

HINWEIS: Ich bin nicht sicher, wie gut diese Lösung funktioniert, wenn dieselben Zeilen mehr als einmal in derselben Datei vorkommen.

Micheal Johnson
quelle