Wie kann ich diff so einstellen, dass nur hinzugefügte und gelöschte Zeilen angezeigt werden? Wenn diff es nicht kann, welches Tool kann es?


69

Wie kann ich diff so einstellen, dass nur hinzugefügte und gelöschte Zeilen angezeigt werden? Wenn diff es nicht kann, welches Tool kann es?


2
Sie müssen besser definieren, was Sie mit "hinzugefügt" und "gelöscht" meinen. Kann sich eine Zeile ändern? Wenn ja, wie soll eine geänderte Leitung behandelt werden? Wenn Sie streng zeilenorientiert prüfen, ist ein Zeilenwechsel identisch mit dem Entfernen der alten und dem Hinzufügen der neuen Zeile. Wie soll es beispielsweise mit einer zweigeteilten Zeile umgehen? Als zwei 1 Zeile gewechselt? 2 Zeilen geändert? 1 Zeile entfernt und 2 Zeilen hinzugefügt? Wenn Sie nicht garantieren können, dass sich Zeilen niemals ändern, sondern nur hinzugefügt und gelöscht werden, ist dies meines Erachtens zum Scheitern verurteilt, ohne bessere Definitionen.
Christopher Cashell

Ich finde die Frage ziemlich unklar. Mindestens eine Interpretation der Frage konnte jedoch mitdiff A B | grep '^[<>]'
kasperd

Möglicherweise suchen Sie nach comm.
Jenny D

@ChristopherCashell, Er bedeutet Sortierreihenfolge ignorieren; ein typisch häufiges Problem. Normalerweise werden dazu zuerst die Segmente (Linien) auf jeder Seite sortiert, bevor ein typisches Diff durchgeführt wird.
Pacerier

@ Pacerier, bist du dir da sicher? Oder raten Sie? In der Frage wird nichts über Sortierung oder Suchreihenfolge erwähnt oder angedeutet. Im Moment ist die Frage nicht klar und kann auf viele verschiedene Arten interpretiert werden. Ohne genau zu wissen , wonach er fragt, gehen wir von Annahmen aus und bieten Lösungen an, die das eigentliche Problem möglicherweise lösen oder nicht. Darüber hinaus deutet der Kommentar des ursprünglichen Posters zu einer der Antworten darauf hin, dass dies nicht mit dem Sortieren zusammenhängt. Es hat mit der Bedeutung von "hinzugefügt und gelöscht" vs. "geändert" zu tun.
Christopher Cashell

Antworten:


82

Versuchen Sie es mit comm

Eine andere Sichtweise:

  • Zeilen anzeigen, die nur in Datei a existieren: (dh was aus a gelöscht wurde)

    comm -23 a b
    
  • Zeige Zeilen, die nur in Datei b existieren: (dh was wurde zu b hinzugefügt)

    comm -13 a b
    
  • Zeige Zeilen, die nur in der einen oder der anderen Datei existieren: (aber nicht in beiden)

    comm -3 a b | sed 's/^\t//'
    

(Warnung: Wenn eine Datei aZeilen enthält, die mit TAB beginnen, wird sie (die erste TAB) aus der Ausgabe entfernt.)

Nur sortierte Dateien

HINWEIS: Beide Dateien müssen sortiert sein, damit commsie ordnungsgemäß funktionieren. Wenn sie noch nicht sortiert sind, sollten Sie sie sortieren:

sort <a >a.sorted
sort <b >b.sorted
comm -12 a.sorted b.sorted

Wenn die Dateien extrem lang sind, kann dies eine ziemliche Belastung sein, da eine zusätzliche Kopie und damit doppelt so viel Speicherplatz erforderlich ist.


5
wollte nur hinzufügen, dass beide Dateien sortiert werden müssen (Groß- und Kleinschreibung beachten), damit diese Lösung korrekte Ergebnisse
liefert

1
Auf modern genug Muscheln können Sie in-line mit etwas wie sortierencomm -12 <(sort a) <(sort b)
Joshua Huber

14

commkönnte machen was du willst. Von seiner Manpage:

BESCHREIBUNG

Vergleichen Sie die sortierten Dateien FILE1 und FILE2 zeilenweise.

Ohne Optionen können Sie dreispaltige Ausgaben erstellen. Spalte eins enthält Zeilen, die für DATEI1 eindeutig sind, Spalte zwei enthält Zeilen, die für DATEI2 eindeutig sind, und Spalte drei enthält Zeilen, die für beide Dateien gleich sind.

Diese Spalten sind unterdrückbar mit -1, -2und -3jeweils.

Beispiel:

[root@dev ~]# cat a
common
shared
unique

[root@dev ~]# cat b
common
individual
shared

[root@dev ~]# comm -3 a b
    individual
unique

Und wenn Sie nur die eindeutigen Zeilen möchten und es Ihnen egal ist, in welcher Datei sie sich befinden:

[root@dev ~]# comm -3 a b | sed 's/^\t//'
individual
unique

Wie in der Manpage steht, müssen die Dateien vorher sortiert werden.


9

Anzeigen von Hinzufügungen und Löschungen ohne Kontext, Zeilennummern, +, -, <,>! usw. können Sie diff wie folgt verwenden:

diff --changed-group-format='%<%>' --unchanged-group-format='' a.txt b.txt 

Zum Beispiel zwei Dateien gegeben:

a.txt

Common
Common
A-ONLY
Common

b.txt

Common
B-ONLY
Common
Common

Der folgende Befehl zeigt Zeilen an, die entweder aus a entfernt oder zu b hinzugefügt wurden:

diff --changed-group-format='%<%>' --unchanged-group-format='' a.txt b.txt 

Ausgabe:

B-ONLY
A-ONLY

Dieser etwas andere Befehl zeigt Zeilen an, die aus a.txt entfernt wurden:

diff --changed-group-format='%<' --unchanged-group-format='' a.txt b.txt 

Ausgabe:

A-ONLY

Schließlich zeigt dieser Befehl Zeilen an, die zu a.txt hinzugefügt wurden

diff --changed-group-format='%>' --unchanged-group-format='' a.txt b.txt 

Ausgabe

B-ONLY

2

Das ist, was diff standardmäßig tut ... Vielleicht müssen Sie einige Flags hinzufügen, um Whitespace zu ignorieren?

diff -b -B

sollten Leerzeilen und unterschiedlich viele Leerzeichen ignorieren.


1
Nein, es werden auch GEÄNDERTE Zeilen angezeigt (Zeilen mit einem oder vier verschiedenen Zeichen). Ich möchte Linien, die nur links oder rechts existieren.
C. Ross

2
Man könnte argumentieren, dass die unterschiedlichen Versionen einer CHANGED-Datei jeweils nur links oder rechts existieren.
Markdrayton

2
Es gibt keine Möglichkeit für diff (oder ein anderes Tool), zuverlässig zu erkennen, was sich geändert hat und was eine gelöschte Zeile durch eine neue Zeile ersetzt.
Cian,

1
Technisch betrachtet behandelt diff eine "geänderte" Zeile so, als ob die ursprüngliche Zeile gelöscht und eine neue Zeile hinzugefügt worden wäre. Technisch zeigt es Ihnen also nur hinzugefügte und gelöschte Zeilen.
KFro

2

Nein, diffzeigt die Unterschiede zwischen zwei Dateien nicht so, wie man denkt. Es erzeugt eine Folge von Bearbeitungsbefehlen für ein Werkzeug patch, mit dem Sie eine Datei in eine andere ändern möchten.

Die Schwierigkeit für jeden Versuch, das zu tun, wonach Sie suchen, besteht darin, zu definieren, was eine geänderte Zeile gegenüber einer gelöschten Zeile und einer hinzugefügten Zeile darstellt. Außerdem, was zu tun ist, wenn Zeilen nebeneinander hinzugefügt, gelöscht und geändert werden.


Genau meine Gedanken. Wie viel Prozent der Zeichen in einer Zeile müssen geändert werden, um sie anstelle einer Änderung des Originals als neu zu betrachten? Selbst wenn Sie ein Zeichen gemeinsam haben, können Sie es technisch als "Änderung" anstatt als Löschen und Einfügen betrachten.
Kamil Kisiel

1
Es ist lange her, dass ich mir die diffQuellen angesehen habe, aber ich scheine mich an alle möglichen Drehungen zu erinnern, um zu verfolgen, wo zwei Dateien übereinstimmen, um synchron zu bleiben, und ich denke, es gibt eine Schwelle zum Aufgeben, die davon abhängt, wie weit die voneinander entfernt sind Linien sind. Aber ich erinnere mich an keinen Zeilenabgleich, außer an (optional) reduzierte Leerzeichen oder ignorierte Groß- und Kleinschreibung. Oder (vielleicht) Worte zu diesem Thema. Auf jeden Fall geht es darum, patchund "vgrep" kommt nur für die Fahrt mit. Vielleicht. Am Dienstag.
Dennis Williamson

2

Visuelle Vergleichstools passen zwei Dateien zusammen, sodass ein Segment mit der gleichen Anzahl von Zeilen, aber unterschiedlichem Inhalt als geändertes Segment betrachtet wird. Völlig neue Zeilen zwischen übereinstimmenden Segmenten werden als hinzugefügte Segmente betrachtet.

So funktioniert auch das Befehlszeilentool sdiff , mit dem zwei Dateien in einem Terminal nebeneinander verglichen werden. Geänderte Zeilen werden durch | getrennt Charakter. Wenn eine Zeile nur in Datei A vorhanden ist, wird <als Trennzeichen verwendet. Wenn eine Zeile nur in Datei B vorhanden ist, wird> als Trennzeichen verwendet. Wenn die Dateien keine <und> Zeichen enthalten, können Sie dies verwenden, um nur hinzugefügte Zeilen anzuzeigen:

sdiff A B | grep '[<>]'

2

Danke Senarvi, deine Lösung (nicht gewählt) hat mir GENAU das gegeben, was ich wollte, nachdem ich jahrelang auf einer Tonne Seiten gesucht hatte.

Nach Ihrer Antwort habe ich mir Folgendes ausgedacht, um die Liste der Dinge zu ändern / hinzuzufügen / zu löschen. Das Beispiel verwendet zwei Versionen der Datei / etc / passwd und druckt den Benutzernamen für die relevanten Datensätze aus.

#!/bin/bash
sdiff passwd1 passwd2 | grep '[|]' | awk -F: '{print "changed: " $1}'
sdiff passwd1 passwd2 | grep '[<]' | awk -F: '{print "deleted: " $1}'
sdiff passwd1 passwd2 | grep '[>]' | awk -F\> '{print $2}' | awk -F: '{print "added: " $1}'

Beachten Sie, dass der Unterschied zwischen "eine Zeile wurde geändert" und "eine Zeile wurde entfernt und eine andere Zeile wurde darunter oder darüber hinzugefügt" semantisch ist. Ein generisches textbasiertes Diff-Tool kann diese Fälle nicht trennen. Infolgedessen kann Ihre sdiff-basierte Antwort nicht in allen Fällen zuverlässig funktionieren.
Mikko Rantalainen

0

Ich finde diese spezielle Form oft nützlich:

diff --changed-group-format='-%<+%>' --unchanged-group-format='' f g

Beispiel:

printf 'a\nb\nc\nd\ne\nf\ng\n' > f
printf 'a\nB\nC\nd\nE\nF\ng\n' > g
diff --old-line-format=$'-%l\n' \
     --new-line-format=$'+%l\n' \
     --unchanged-line-format='' \
     f g

Ausgabe:

-b
-c
+B
+C
-e
-f
+E
+F

Es werden also alte Zeilen mit -gefolgt von der entsprechenden neuen Zeile mit angezeigt +.

Wenn wir eine Löschung von C:

printf 'a\nb\nd\ne\nf\ng\n' > f
printf 'a\nB\nC\nd\nE\nF\ng\n' > g
diff --old-line-format=$'-%l\n' \
     --new-line-format=$'+%l\n' \
     --unchanged-line-format='' \
     f g

es sieht aus wie das:

-b
+B
+C
-e
-f
+E
+F

Das Format ist dokumentiert unter man diff:

       --line-format=LFMT
              format all input lines with LFMT`

und:

       LTYPE is 'old', 'new', or 'unchanged'.
              GTYPE is LTYPE or 'changed'.

und:

              LFMT (only) may contain:

       %L     contents of line

       %l     contents of line, excluding any trailing newline

       [...]

Zugehörige Frage: https://stackoverflow.com/questions/15384818/wie-den- Unterschied- nur- zwischen-zwei- Dateien-in-linux- additionieren

Getestet in Ubuntu 18.04.


-1

Datei1:

text670_1
text067_1
text067_2

Datei2:

text04_1
text04_2
text05_1
text05_2
text067_1
text067_2
text1000_1

Verwenden:

diff -y file1 file2

Hier werden zwei Spalten für Repectives-Dateien angezeigt.

Ausgabe:

text670_1                           
                                  > text04_1
                                  > text04_2
                                  > text05_1
                                  > text05_2
text067_1                           text67_1
text067_2                           text67_2
                                  > text1000_1
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.