Jak różnicować pliki ignorując komentarze (linie zaczynające się od #)?

55

Mam dwa pliki konfiguracyjne, oryginalny od menedżera pakietów i dostosowany przeze mnie. Dodałem kilka komentarzy opisujących zachowanie.

Jak mogę uruchomić diffpliki konfiguracyjne, pomijając komentarze? Skomentowany wiersz jest definiowany przez:

  • opcjonalne wiodące białe znaki (tabulatory i spacje)
  • znak skrótu ( #)
  • cokolwiek innego

Najprostszym wyrażeniem regularnym byłoby pominięcie pierwszego wymagania #.*. Próbowałem --ignore-matching-lines=RE( -I RE) opcji GNU diff 3.0, ale nie mogłem uzyskać pracy z tym RE. Próbowałem też .*#.*i .*\#.*bez powodzenia. Dosłowne umieszczenie line ( Port 631) jako REnic nie pasuje, ani nie pomaga umieszczać RE między ukośnikami.

Jak sugeruje to w „diff” smak wyrażenia regularnego wydaje się brakować? , Próbowałem grep -G:

grep -G '#.*' file

To wydaje się pasować do komentarzy, ale nie działa diff -I '#.*' file1 file2.

Jak więc korzystać z tej opcji? Jak mogę diffpominąć niektóre wiersze (w moim przypadku komentarze)? Proszę nie sugerować greppliku i porównywać plików tymczasowych.

Lekensteyn
źródło
12
Ta -Iopcja powoduje, że blok jest ignorowany tylko wtedy, gdy wszystkie jego wiersze są zgodne z wyrażeniem regularnym. Możesz więc w ten sposób zignorować zmianę tylko do komentowania, ale nie zmiany do komentowania, które są zbliżone do zmiany bez komentowania.
Gilles „SO- przestań być zły”
@Gilles: Dzięki, teraz rozumiem, dlaczego diff -Inie zachowuje się tak, jak się spodziewałem. Zaktualizowałem swoją odpowiedź przykładem, który wyjaśnił mi to zachowanie.
Lekensteyn,

Odpowiedzi:

49

Według Gillesa -Iopcja ignoruje linię tylko wtedy, gdy nic poza tym zestawem nie pasuje, z wyjątkiem dopasowania -I. Nie w pełni go zrozumiałem, dopóki go nie przetestowałem.

Test

W mój test biorą udział trzy pliki:
Plik test1:

    text

Plik test2:

    text
    #comment

Plik test3:

    changed text
    #comment

Polecenia:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment

Alternatywny sposób

Ponieważ do tej pory nie ma odpowiedzi wyjaśniającej, jak -Iprawidłowo korzystać z tej opcji, przedstawię alternatywę, która działa w powłokach bash:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - zunifikowany diff
    • -B - zignoruj ​​puste linie
  • <(command)- funkcja bash zwana podstawieniem procesu, która otwiera deskryptor pliku dla polecenia, eliminuje to potrzebę pliku tymczasowego
  • grep - polecenie drukowania linii (nie) pasujących do wzoru
    • -v - pokaż niepasujące linie
    • E - używaj rozszerzonych wyrażeń regularnych
    • '^\s*(#|$)' - wyrażenie regularne pasujące do komentarzy i pustych linii
      • ^ - dopasuj początek linii
      • \s* - dopasuj białe znaki (tabulatory i spacje), jeśli występują
      • (#|$) dopasuj znak krzyżyka lub alternatywnie koniec linii
Lekensteyn
źródło
6

Próbować:

diff -b -I '^#' -I '^ #' file1 file2

Pamiętaj, że regex musi pasować do odpowiedniej linii w obu plikach i pasuje do każdej zmienionej linii w przystojniaku, aby działać, w przeciwnym razie nadal będzie wyświetlać różnicę.

Używaj pojedynczych cudzysłowów, aby zabezpieczyć wzór przed rozszerzaniem powłoki i uniknąć znaków zarezerwowanych dla wyrażenia regularnego (np. Nawiasów).

Możemy przeczytać w diffutilsinstrukcji:

Jednak -Iignoruje wstawianie lub usuwanie wierszy zawierających wyrażenie regularne, jeśli każda zmieniona linia w przystojniaku (każde wstawienie i każde usunięcie) jest zgodna z wyrażeniem regularnym.

Innymi słowy, dla każdej nieusuwalnej zmiany diffdrukuje pełny zestaw zmian w jej pobliżu, w tym zmiany niezapomniane. Możesz podać więcej niż jedno wyrażenie regularne dla linii do zignorowania, używając więcej niż jednej -Iopcji. diffpróbuje dopasować każdą linię do każdego wyrażenia regularnego, zaczynając od ostatniego podanego.

Zachowanie to jest również dobrze wyjaśnione przez Armel .

Powiązane: Jak mogę wykonać różnicę, która ignoruje wszystkie komentarze?

kenorb
źródło
2

Po przeszukaniu sieci, alternatywny sposób Lekensteyn jest lepszy.

Ale chcę użyć wyjścia dif jako łaty ... i jest problem, ponieważ numery wierszy są przechowywane z powodu "grep -v".

Dlatego zamierzam ulepszyć ten wiersz poleceń:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)

Nie jest idealny, ale numer linii jest przechowywany w pliku łatki.

Jeśli jednak zostanie dodany nowy wiersz zamiast wiersza komentarza ... komentarz spowoduje wygenerowanie przystawki FAILED podczas poprawiania, jak widać poniżej.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

przetestuj teraz nasze polecenie

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line

-other text
+other text changed

/ dev / fd / 62 i / dev / fd / 63 to pliki tworzone przez podstawienie procesu. Linia między „+ nową linią” a „-innym tekstem” jest domyślnym znakiem spacji zdefiniowanym w naszym wyrażeniu sed w celu zastąpienia komentarzy.

A teraz, co nadchodzi, kiedy zastosujemy tę łatkę:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej

Rozwiązaniem jest nie używać ujednoliconego formatu różnic bez -u

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed

teraz popraw plik roboczy pliku (bez gwarancji wyniku w bardzo złożonym procesie różnicowym).

syjust
źródło
Twój zunifikowany plik różnicowy nie działa z powodu różnic kontekstu. Możesz użyć, diff -U0 one twoaby wyłączyć kontekst. Do łatania jest kilka narzędzi, które mogą być bardziej odpowiednie, takich jak kdiff3.
Lekensteyn,
Dziękujemy za -U0opcję wyłączenia kontekstu. Uwaga: kdiff3 to narzędzie graficzne. Potrzebuję automatycznego narzędzia do zarządzania atrybutami scalania git.
syjust
vimdiffobsługuje połączenia trójstronne, być może warto się przyjrzeć.
Lekensteyn,
dokładniej, potrzebuję narzędzia skryptowego do automatyzacji procesu scalania git z wykluczeniami w skrypcie SQL. kdiff3 i vimdiff to narzędzia interaktywne, które w moim przypadku nie są użyteczne.
syjust
1

Zwykle ignoruję ten bałagan przez:

  • Generowanie wersji bez komentarzy przy użyciu grep -v "^#" | cat -si różnicowanie tych lub ...
  • Używanie vim -ddo przeglądania plików. Podświetlanie składni powoduje, że różnice między komentarzem a nie-komentarzem są dość oczywiste. Różnicowanie różnic w linii, dzięki czemu można zobaczyć, które wartości lub części wartości zostały zmienione na pierwszy rzut oka, sprawia, że ​​jest to moja ulubiona.
Caleb
źródło
0

Oto, czego używam, aby usunąć wszystkie skomentowane linie - nawet te zaczynające się tabulatorem lub spacją - i puste:

egrep -v "^$|^[[:space:]]*#" /path/to/file

lub możesz zrobić

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
Philomath
źródło