Zapobiegaj różnicom w sprawdzaniu nowej linii na końcu pliku

21

Mam dwa duże drzewa, które chcę porównać. Niektóre pliki w drzewie różnią się tylko dlatego, że jeden ma nowy wiersz na końcu, a drugi plik nie ma tego nowego wiersza. Chcę zignorować ten fakt. Próbowałem dzwonić w difften sposób:

diff --ignore-all-space -r <dir1> <dir2>

I to działa. Mój problem polega na tym, że ignoruje także inne różnice (związane z przestrzenią), które mogą być ważne.

Podsumowując: ja po prostu chcą ignorować przełamane na EOF. Czy to jest możliwe diff?

Dangonfast
źródło

Odpowiedzi:

17

Zasadniczo musisz porównać dwa pliki, warunkowo ignorując końcowy bajt. Nie ma opcji „diff”, aby to zrobić - ale istnieje wiele sposobów, aby to zrobić (np. Przychodzi na myśl również hex diff).

Aby użyć „diff”, musisz po prostu zmodyfikować pliki, które nie mają nowej linii na końcu pliku, a następnie porównać. Możesz utworzyć katalog tymczasowy ze zmodyfikowanymi plikami lub przy odrobinie skryptu można to zrobić w pamięci. (Wybór preferowanego zależy od preferencji, rozmiaru pliku, liczby plików ...)

Na przykład: zmodyfikuje zawartość pliku (użyj sed -ido modyfikacji w miejscu, to po prostu drukuje na standardowe wyjście), aby dodać nowy wiersz, jeśli go brakuje (lub pozostaw plik bez zmian, jeśli już jest nowy wiersz):

sed -e '$a\'  file1.txt

I tylko w celu przejrzenia składni „diff” (zwracanie true oznacza, że ​​są takie same, false oznacza różne):

$ diff a/file1.txt   b/file1.txt  \
      && echo '** are same' || echo '** are different'
2c2
< eof
---
> eof
\ No newline at end of file
** are different

Sprawdź, czy tylko białe znaki są różne:

$ diff --ignore-all-space  a/file1.txt   b/file1.txt \
     && echo '** are same' || echo '** are different'
** are same

W bash możemy użyć „sed” do manipulowania zawartością pliku, który jest przekazywany do „diff” (oryginalne pliki pozostały niezmienione):

$ diff <(sed -e '$a\' a/file1.txt) <(sed -e '$a\' b/file1.txt) \
     && echo '** are same' || echo '** are different'
** are same

Teraz wystarczy naśladować diff -rrekurencyjne porównywanie katalogów. W przypadku porównywania katalogów ai b, to dla wszystkich plików a(na przykład a/dir1/dir2/file.txt) nie czerpią ścieżkę do pliku w b(na przykład b/dir1/dir2/file.txt) i porównać:

$ for f in $( find a -type f  )
> do
>    diff <(sed -e '$a\' $f) <(sed -e '$a\' b/${f#*/})
> done

Nieco bardziej pełna wersja:

$ for f in $( find a -type f  )
> do
>   f1=$f
>   f2=b/${f#*/}
>   echo "compare: $f1 $f2"
>   diff <(sed -e '$a\' $f1) <(sed -e '$a\' $f2) \
>       && echo '** are same' || echo '** are different'
> done && echo '** all are same' || echo '** all are different'
compare: a/file1.txt b/file1.txt
** are same
compare: a/file2.txt b/file2.txt
** are same
** all are same
Michał
źródło
czy mógłbyś wyjaśnić, co sed -e '$a\'dokładnie robi? thx
törzsmókus
uruchom sed, biorąc pod uwagę następujący -eskrypt / wyrażenie, które pasuje do końca pliku ( $), i wykonaj akcję „append” (a \), ale tak naprawdę nie określaj tekstu (nic po `\`), który nadal doda EOF / nowy wiersz na końcu pliku (tylko jeśli go brakuje).
Michael
dzięki. Jeszcze nie widziałem a\ .
törzsmókus
1

Rozwiązałem problem, dodając nowy wiersz do każdego pliku i ignorując puste linie w diff (opcja -B). Te rozwiązania mogą nie być odpowiednie dla twojego przypadku użycia, ale mogą pomóc innym:

echo >> $FILE1 
echo >> $FILE2
diff -B $FILE1 FILE2 
Jakob
źródło
0

Potokuj wyjście diffdo greppolecenia, które upuszcza komunikat, którego nie chcesz widzieć.

David Schwartz
źródło
niedobrze. diff -r istnieje z wynikiem! = 0, jeśli nie dodam --ignore-all-space. Żeby było jasne: chcę, aby diff ignorował znaki nowej linii w EOF i tylko w EOF. I chcę, aby raportował wynik spełniający te kryteria. Oznacza to, że jeśli pliki w drzewie różnią się tylko nową linią w EOF, nie należy tego uważać za różnicę, a zatem diff musi zwrócić 0.
dangonfast
0

Pomyślałem też o innym podejściu, które będzie działać dla większych plików (i nadal nie kopiuje ani nie modyfikuje oryginalnych plików). Nadal będziesz musiał emulować przechodzenie przez katalog rekurencyjny (istnieje wiele sposobów, aby to zrobić), ale ten przykład nie używa „sed”, ale po prostu porównuje dwa pliki, z wyłączeniem ostatniego bajtu, używając cmpnp.

$ cmp  a/file1.txt  b/file1.txt  && echo '** are same' || echo '** are different'
cmp: EOF on b/file1.txt
** are different

$ du -b a/file1.txt  b/file1.txt 
13  a/file1.txt
12  b/file1.txt

$ cmp  -n 12 a/file1.txt  b/file1.txt  && echo '** are same' || echo '** are different'
** are same

Nadal zapętlaj wszystkie pliki w katalogu, a dla dwóch plików a / file.txt i b / file.txt, oblicz większy rozmiar pliku i odejmij jeden, a następnie wykonaj diff binarny ( cmp) używając tej liczby bajtów (również w grzmotnąć):

(( bytes = $(du -b a/file.txt  b/file.txt  | sort -nr | head -1  | cut -f1) - 1 ))
cmp -n $bytes a/file.txt b/file.txt

Pętle nad plikami byłyby takie same jak w innych odpowiedziach przy użyciu sedi diff.

Michał
źródło
0

Odpowiedź jest prosta.
Komunikat o brakującym nowym wierszu nie znajduje się w strumieniu wyjściowym, diffale w strumieniu błędów. Więc zgnij to do nirwany, a skończysz na dobre

diff -rqEeB fileA fileB 2> /dev/null
Junzen
źródło
diff zwraca wartość! = 0, jeśli znajdzie różnice i chcę tę wartość sprawdzić. Przekierowanie do / dev / null nie powoduje, że diff zapomina o tej różnicy, więc zwrócona wartość to! = 0, czego nie chcę. Chcę, aby diff uznał dwa pliki za równe, jeśli jedyną różnicą jest ostatnia nowa linia
dangonfast
-1

W diff commnad znajduje się flaga: --strip-trailing-crktóra robi dokładnie to, o co prosiłeś

dharman
źródło
-1. Próbowałeś tego? Traktuje /r/njak /ni nie ma nic wspólnego z dodatkowymi /ntuż przed EOF.
Kamil Maciorowski
Próbowałem tego i użyłem go do różnicowania pliku z inną nową linią dos / unix ... prawda?
dharman
Pytanie dotyczy zignorowania nowego wiersza tylko w EOF (koniec pliku).
Kamil Maciorowski