Jak mogę wyświetlić różnicę, aby wyświetlać tylko dodane i usunięte linie? Jeśli diff nie może tego zrobić, jakie narzędzie może?

69

Jak mogę wyświetlić różnicę, aby wyświetlać tylko dodane i usunięte linie? Jeśli diff nie może tego zrobić, jakie narzędzie może?

Krzyż
źródło
2
Musisz lepiej zdefiniować, co masz na myśli, dodając i usuwając. W szczególności, czy linia może się zmienić? Jeśli tak, to jak chcesz obsłużyć zmienioną linię? Jeśli wykonujesz sprawdzanie ściśle zorientowane na linię, zmiana linii jest identyczna z usuwaniem starej linii i dodawaniem nowej linii. Na przykład, jak powinien obsługiwać linię podzieloną na dwie części? Jak zmieniły się dwie linie 1? 2 linie zmienione? Usunięto 1 linię i dodano 2 linie? O ile nie możesz zagwarantować, że wiersze nigdy się nie zmienią, po prostu dodaj i usuń, ale sądzę, że skazanie to na niepowodzenie bez lepszych definicji.
Christopher Cashell
Pytanie wydaje mi się dość niejasne. Ale na przynajmniej jedną interpretację pytania można odpowiedziećdiff A B | grep '^[<>]'
kasperd
Być może szukasz comm.
Jenny D.
@ChristopherCashell, On oznacza ignorowanie porządku sortowania; typowy problem. Zwykle odbywa się to najpierw przez posortowanie segmentów (linii) po każdej stronie przed wykonaniem typowego porównania.
Pacerier
@Pacerier, jesteś tego pewien? A może zgadujesz? W pytaniu nie wspomniano ani nie wskazano nic na temat kolejności sortowania lub wyszukiwania. W tej chwili pytanie nie jest jasne i można je interpretować na wiele różnych sposobów. Nie wiedząc na pewno, o co pyta, przyjmujemy założenia i oferujemy rozwiązania, które mogą rozwiązać problem. Ponadto komentarz oryginalnego plakatu do jednej z odpowiedzi sugeruje, że nie ma to związku z sortowaniem. Ma to związek ze znaczeniem „dodane i usunięte” vs. „zmienione”.
Christopher Cashell

Odpowiedzi:

81

Spróbuj nawiązać połączenie

Inny sposób na to spojrzeć:

  • Pokaż linie, które istnieją tylko w pliku a: (tzn. Co zostało usunięte z a)

    comm -23 a b
    
  • Pokaż linie, które istnieją tylko w pliku b: (tj. Co zostało dodane do b)

    comm -13 a b
    
  • Pokaż linie, które istnieją tylko w jednym lub drugim pliku: (ale nie w obu)

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

(Ostrzeżenie: jeśli plik ama wiersze rozpoczynające się od TAB, to (pierwsza TAB) zostanie usunięty z wyniku).

Tylko posortowane pliki

UWAGA: Aby pliki commdziałały poprawnie, oba pliki muszą zostać posortowane . Jeśli nie są jeszcze posortowane, należy je posortować:

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

Jeśli pliki są bardzo długie, może to stanowić duże obciążenie, ponieważ wymaga dodatkowej kopii, a zatem dwa razy więcej miejsca na dysku.

TomOnTime
źródło
5
chciałem tylko dodać, że oba pliki muszą być posortowane (
wielkość
Na wystarczająco nowoczesnych powłokach można sortować w linii z czymś takimcomm -12 <(sort a) <(sort b)
Joshua Huber
14

commmoże zrobić co chcesz. Ze strony podręcznika:

OPIS

Porównaj posortowane pliki PLIK1 i PLIK2 linia po linii.

Bez opcji utwórz wynik trójkolumnowy. Pierwsza kolumna zawiera wiersze unikalne dla PLIKU 1, druga kolumna zawiera wiersze unikalne dla PLIKU 2, a kolumna trzecia zawiera wiersze wspólne dla obu plików.

Te kolumny są suppressable z -1, -2i -3odpowiednio.

Przykład:

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

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

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

A jeśli chcesz tylko unikatowych linii i nie obchodzi Cię, w którym pliku się znajdują:

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

Jak mówi strona podręcznika, pliki muszą zostać wcześniej posortowane.

markdrayton
źródło
9

Aby wyświetlić uzupełnienia i usunięcia bez kontekstu, numery linii, +, -, <,>! itp., możesz użyć diff w następujący sposób:

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

Na przykład, biorąc pod uwagę dwa pliki:

a.txt

Common
Common
A-ONLY
Common

b.txt

Common
B-ONLY
Common
Common

Następujące polecenie pokaże wiersze usunięte z a lub dodane do b:

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

wynik:

B-ONLY
A-ONLY

To nieco inne polecenie pokaże linie usunięte z a.txt:

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

wynik:

A-ONLY

Na koniec to polecenie wyświetli wiersze dodane do pliku a.txt

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

wynik

B-ONLY
iphonedroid
źródło
2

To właśnie diff domyślnie robi ... Może trzeba dodać kilka flag, aby zignorować białe znaki?

diff -b -B

powinny ignorować puste linie i inną liczbę spacji.

Scott Lundberg
źródło
1
Nie, pokazuje również ZMIENIONE linie (linie, które mają znak lub cztery różne). Chcę linii, które istnieją tylko w lewo lub w prawo.
C. Ross
2
Można argumentować, że każda z różnych wersji pliku ZMIENIONEGO istnieje tylko po lewej lub po prawej stronie.
markdrayton
2
Nie ma sposobu, aby diff (lub jakiekolwiek inne narzędzie) rzetelnie powiedział, co jest zmianą, a co usuniętą linię zastępuje nowa.
Cian
1
Technicznie, diff traktuje „zmienioną” linię tak, jakby pierwotna linia została usunięta, a nowa linia została dodana ... więc technicznie pokazuje tylko dodane i usunięte linie.
KFro
2

Nie, difftak naprawdę nie pokazuje różnic między dwoma plikami w sposób, w jaki można by pomyśleć. Tworzy sekwencję poleceń edycyjnych dla narzędzia takiego jak narzędzie patchdo zmiany jednego pliku na inny.

Trudność każdej próby zrobienia tego, czego szukasz, polega na tym, jak zdefiniować, co stanowi linię, która zmieniła się w porównaniu do linii usuniętej, po której następuje linia dodana. Co również zrobić, gdy wiersze są dodawane, usuwane i zmieniane obok siebie.

Dennis Williamson
źródło
Dokładnie moje myśli. Jaki procent znaków w wierszu musi się zmienić, aby uznać go za nowy zamiast modyfikacji oryginału? Technicznie, nawet jeśli masz jeden wspólny znak, możesz uznać to za „zmianę” zamiast usuwania i wstawiania.
Kamil Kisiel,
1
Minęło sporo czasu, odkąd spojrzałem na diffźródła, ale wydaje mi się, że pamiętam wszystkie rodzaje zawirowań, aby śledzić, gdzie dwa pliki pasują do siebie w synchronizacji i myślę, że istnieje próg rezygnacji na podstawie tego, jak daleko od siebie są linie są. Ale nie pamiętam żadnego dopasowania wewnątrz linii, z wyjątkiem (opcjonalnie) zwiniętej białej spacji lub ignorowania wielkości liter. Lub (być może) słowa na to wpływają. W każdym razie chodzi o to patchi „vgrep” pojawia się na przejażdżkę. Może. We wtorek.
Dennis Williamson,
2

Narzędzia porównywania wizualnego pasują do siebie dwa pliki, dzięki czemu segment o tej samej liczbie linii, ale o różnej zawartości będzie uważany za zmieniony segment. Całkowicie nowe linie między pasującymi segmentami są uważane za segmenty dodane.

Tak działa również narzędzie wiersza polecenia sdiff , które pokazuje porównanie dwóch plików w terminalu. Zmienione linie są oddzielone | postać. Jeśli wiersz istnieje tylko w pliku A, <jest stosowany jako znak separatora. Jeśli linia istnieje tylko w pliku B,> jest używany jako separator. Jeśli nie masz w plikach znaków <i>, możesz użyć tego, aby wyświetlić tylko dodane linie:

sdiff A B | grep '[<>]'
Seppo Enarvi
źródło
2

Dzięki senarvi, twoje rozwiązanie (nie głosowałem) faktycznie dało mi DOKŁADNIE to, czego chciałem po szukaniu wieku na mnóstwie stron.

Korzystając z twojej odpowiedzi, oto, co wymyśliłem, aby uzyskać listę rzeczy zmienionych / dodanych / usuniętych. W przykładzie użyto 2 wersji pliku / etc / passwd i wypisano nazwę użytkownika dla odpowiednich rekordów.

#!/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}'
genialność
źródło
Zauważ, że ponieważ różnica między „linią została zmodyfikowana” a „linią została usunięta, a inna linia została dodana poniżej lub powyżej” jest semantyczna. Ogólne narzędzie różnicowania oparte na tekście nie może rozdzielić tych przypadków. W rezultacie twoja odpowiedź oparta na sdiff nie może niezawodnie działać we wszystkich przypadkach.
Mikko Rantalainen
0

Uważam, że ta konkretna forma jest często przydatna:

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

Przykład:

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

Wynik:

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

Pokazuje więc stare linie, -po których następuje bezpośrednio nowa linia z +.

Gdybyśmy usunęli 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

To wygląda tak:

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

Format jest udokumentowany na man diff:

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

i:

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

i:

              LFMT (only) may contain:

       %L     contents of line

       %l     contents of line, excluding any trailing newline

       [...]

Powiązane pytanie: https://stackoverflow.com/questions/15384818/how-to-get-the-difference-only-additions-between-two-files-in-linux

Testowane w Ubuntu 18.04.

Ciro Santilli
źródło
-1

Plik 1:

text670_1
text067_1
text067_2

Plik2:

text04_1
text04_2
text05_1
text05_2
text067_1
text067_2
text1000_1

Posługiwać się:

diff -y file1 file2

To pokazuje dwie kolumny dla plików powtórzeń.

Wynik:

text670_1                           
                                  > text04_1
                                  > text04_2
                                  > text05_1
                                  > text05_2
text067_1                           text67_1
text067_2                           text67_2
                                  > text1000_1
Adriano
źródło