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.


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

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?


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).


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.

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.