Vergleichen Sie zwei Dateien Zeile für Zeile und generieren Sie den Unterschied in einer anderen Datei

120

Ich möchte Datei1 mit Datei2 vergleichen und eine Datei3 generieren, die die Zeilen in Datei1 enthält, die in Datei2 nicht vorhanden sind.

Balualways
quelle
Ich habe diff ausprobiert, aber es werden einige Zahlen und andere Symbole vor verschiedenen Zeilen generiert, was mir den Vergleich von Dateien erschwert.
So

Antworten:

215

diff (1) ist nicht die Antwort, aber comm (1) ist.

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

So

comm -2 -3 file1 file2 > file3

Die Eingabedateien müssen sortiert werden. Wenn dies nicht der Fall ist, sortieren Sie sie zuerst. Dies kann mit einer temporären Datei erfolgen oder ...

comm -2 -3 <(sort file1) <(sort file2) > file3

vorausgesetzt, Ihre Shell unterstützt die Prozessersetzung (bash tut dies).

sorpigal
quelle
1
Denken Sie daran , dass zwei Dateien sortiert werden müssen und ist einzigartig
andy
6
Sie können die Optionen zusammenfassen:comm -23
Paolo M
Was bedeutet "sortiert"? Dass die Zeilen die gleiche Reihenfolge haben? Dann ist es wahrscheinlich für die meisten Anwendungsfälle in Ordnung - wie in der Überprüfung, welche Zeilen hinzugefügt wurden, indem Sie sie mit einer gesicherten älteren Version vergleichen. Wenn neu hinzugefügte Zeilen nicht zwischen den vorhandenen Zeilen liegen können, ist dies eher ein Problem.
Egor Hans
@EgorHans: Wenn die Datei zB Zeilen enthält, die Ganzzahlen wie "3 \ n1 \ n3 \ n2 \ n" enthalten, müssen die Zeilen zuerst in aufsteigender oder absteigender Reihenfolge neu angeordnet werden, z. B. "\ 1 \ n2 \ n3 \ n3 \ n" mit Duplikaten benachbart. Das ist "sortiert" und beide Dateien müssen auf ähnliche Weise sortiert werden. Wenn die neuere Datei neue Zeilen enthält, spielt es keine Rolle, ob sie "zwischen vorhandenen Zeilen" liegen, da sie nach der Sortierung nicht in sortierter Reihenfolge angezeigt werden.
Sorpigal
47

Das Unix-Dienstprogramm diffist genau für diesen Zweck gedacht.

$ diff -u file1 file2 > file3

Optionen, verschiedene Ausgabeformate usw. finden Sie im Handbuch und im Internet.

Thanatos
quelle
7
Das macht den angeforderten Job nicht; Es werden eine Reihe zusätzlicher Zeichen eingefügt, selbst wenn Befehlszeilenschalter verwendet werden, die in anderen Antworten vorgeschlagen werden.
Xenocyon
20

Betrachten Sie diese:
Datei a.txt:

abcd
efgh

Datei b.txt:

abcd

Sie können den Unterschied finden mit:

diff -a --suppress-common-lines -y a.txt b.txt

Die Ausgabe wird sein:

efgh 

Sie können die Ausgabe in einer Ausgabedatei (c.txt) umleiten, indem Sie:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

Dies wird Ihre Frage beantworten:

"... die die Zeilen in Datei1 enthält, die in Datei2 nicht vorhanden sind."

Neilvert Noval
quelle
2
Diese Antwort unterliegt zwei Einschränkungen: (1) Sie funktioniert nur für kurze Zeilen (standardmäßig weniger als 80 Zeichen, obwohl dies geändert werden kann), und, was noch wichtiger ist, (2) am Ende wird jeweils ein "<" hinzugefügt Zeile, die mit einem anderen Programm entfernt werden muss (z. B. awk, sed).
Sergut
In vielen Fällen möchten Sie auch verwenden -d, diffum das kleinstmögliche Diff zu finden. -i, -E, -w, -BUnd --suppress-blank-emptykann auch gelegentlich nützlich, wenn auch nicht immer sein. Wenn Sie nicht wissen, was zu Ihrem Anwendungsfall passt, versuchen Sie es diff --helpzuerst (was im Allgemeinen eine gute Idee ist, wenn Sie nicht wissen, was ein Befehl tun kann).
Egor Hans
Wenn Sie --line-format =% L verwenden, verhindern Sie außerdem, dass diff zusätzliche Zeichen generiert (zumindest heißt es in der Hilfe, dass dies so funktioniert, Sie es jedoch gleich ausprobieren werden).
Egor Hans
Auch dies ist kürzer und scheint das gleiche zu funktionieren stackoverflow.com/a/27667185/1179925
mrgloom
8

Manchmal diffist das Dienstprogramm, das Sie benötigen, aber manchmal joinist es angemessener. Die Dateien müssen vorsortiert sein. Wenn Sie eine Shell verwenden, die die Prozessersetzung wie bash, ksh oder zsh unterstützt, können Sie die Sortierung im laufenden Betrieb durchführen.

join -v 1 <(sort file1) <(sort file2)
Bis auf weiteres angehalten.
quelle
Sie sollten eine Medaille dafür bekommen! Es war genau das, wonach ich in den letzten 2 Stunden
gesucht habe
7

Versuchen

sdiff file1 file2

Normalerweise funktioniert es bei mir in den meisten Fällen viel besser. Möglicherweise möchten Sie Dateien vorher sortieren, wenn die Reihenfolge der Zeilen nicht wichtig ist (z. B. einige Textkonfigurationsdateien).

Beispielsweise,

sdiff -w 185 file1.cfg file2.cfg
Tagar
quelle
1
Nettes Dienstprogramm! Ich liebe es, wie es die Unterscheidungslinien markiert. Erleichtert den Vergleich von Konfigurationen erheblich. Dies zusammen mit sort ist eine tödliche Kombination (zB sdiff <(sort file1) <(sort file2))
jmagnusson
3

Wenn Sie dies mit Coreutils lösen müssen, ist die akzeptierte Antwort gut:

comm -23 <(sort file1) <(sort file2) > file3

Sie können auch sd (stream diff) verwenden, das weder sortiert noch ersetzt werden muss und unendliche Streams unterstützt, wie z.

cat file1 | sd 'cat file2' > file3

Wahrscheinlich kein so großer Vorteil für dieses Beispiel, aber denken Sie trotzdem darüber nach. In einigen Fällen können Sie commweder grep -Fnoch verwenden diff.

Hier ist ein Blogpost, den ich über unterschiedliche Streams auf dem Terminal geschrieben habe und in dem sd vorgestellt wird.

mlg
quelle
3

Noch keine grepLösung?

  • Zeilen, die nur in Datei2 vorhanden sind:

    grep -Fxvf file1 file2 > file3
  • Zeilen, die nur in Datei1 vorhanden sind:

    grep -Fxvf file2 file1 > file3
  • Zeilen, die in beiden Dateien vorhanden sind:

    grep -Fxf file1 file2 > file3
αғsнιη
quelle
2

Viele Antworten bereits, aber keine von ihnen perfekt IMHO. Die Antwort von Thanatos hinterlässt einige zusätzliche Zeichen pro Zeile und die Antwort von Sorpigal erfordert, dass die Dateien sortiert oder vorsortiert werden, was möglicherweise nicht unter allen Umständen angemessen ist.

Ich denke , der beste Weg , um die Linien zu bekommen , die sonst anders und nichts sind (keine zusätzlichen Zeichen, keine Nachbestellung) ist eine Kombination aus diff, grepund awk(oder ähnliches).

Wenn die Zeilen kein "<" enthalten, kann ein kurzer Einzeiler sein:

diff urls.txt* | grep "<" | sed 's/< //g'

Dadurch wird jedoch jede Instanz von "<" (weniger als Leerzeichen) aus den Zeilen entfernt, was nicht immer in Ordnung ist (z. B. Quellcode). Die sicherste Option ist die Verwendung von awk:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

Dieser Einzeiler unterscheidet beide Dateien, filtert dann die Ausgabe von diff im ed-Stil heraus und entfernt dann das nachfolgende "<", das diff hinzufügt. Dies funktioniert auch dann, wenn die Zeilen selbst ein "<" enthalten.

Sergut
quelle
1
comm erfordert keine Sortierung (in neueren Versionen?) - verwenden Sie einfach --nocheck-order. Ich benutze dies oft, wenn ich CSVs von der CLI aus manipuliere
ak5
2

Ich bin überrascht, dass niemand erwähnt hat diff -y, dass er nebeneinander ausgegeben werden soll , zum Beispiel:

diff -y file1 file2 > file3

Und in file3(verschiedene Zeilen haben ein Symbol |in der Mitte):

same     same
diff_1 | diff_2
xtluo
quelle
1

Verwenden Sie das Dienstprogramm Diff und extrahieren Sie nur die Zeilen, die in der Ausgabe mit <beginnen

Capslockk
quelle
0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

Ich habe fast alle Antworten in diesem Thread ausprobiert, aber keine war vollständig. Nach ein paar Trails oben hat einer für mich gearbeitet. Diff gibt Ihnen einen Unterschied, aber mit einigen unerwünschten speziellen Charas. wo Sie tatsächliche Differenzlinien beginnen mit '>'. Der nächste Schritt besteht darin, die Zeilen mit '>' zu erfassen und anschließend mit sed zu entfernen .

Tollin Jose
quelle
1
Das ist eine schlechte Idee. Sie müssten auch Zeilen ändern, die mit beginnen <. Sie sehen dies, wenn Sie die Reihenfolge der Eingabedateien vertauschen. Selbst wenn Sie dies tun würden, würden Sie es grepmit mehr sed weglassen wollen : `diff a1 a2 | sed '/> / s ///' `Dies kann immer noch Zeilen mit >oder <in der richtigen Situation unterbrechen und es bleiben zusätzliche Zeilen übrig, die die Zeilennummern beschreiben. Wenn Sie diesen Ansatz ausprobieren möchten, wäre ein besserer Weg : diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'.
Sorpigal
0

Sie können diffmit folgenden Ausgabeformatierungen verwenden:

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format=''Deaktivieren Sie die Ausgabe für Datei1, wenn die Zeile unterschiedlich war. Vergleichen Sie sie in Datei2.
--unchanged-line-format=''Deaktivieren Sie die Ausgabe, wenn die Zeilen identisch sind.

αғsнιη
quelle