20

У меня есть два больших дерева, которые я хочу сравнить. Некоторые из файлов в дереве отличаются только тем, что в конце у одного есть символ новой строки, а у другого файла этого символа нет. Я хочу игнорировать этот факт. Я попытался вызвать diff как это:

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

И это работает. Моя проблема в том, что он также игнорирует другие различия (связанные с пространством), которые могут быть важны.

В заключение: я просто хочу игнорировать перевод строки в EOF. Это возможно с diff?

6 ответов6

16

Вам нужно сравнить два файла, условно игнорируя завершающий байт. Для этого нет опции 'diff' - но есть несколько способов сделать это (например, на ум приходит hex diff).

Чтобы использовать 'diff', вам нужно изменить файлы, в которых отсутствует символ новой строки в конце файла, а затем сравнить. Вы можете создать временный каталог с измененными файлами, или с помощью небольшого количества сценариев это можно сделать в памяти. (То, что является предпочтительным, зависит от предпочтения, размера файла, количества файлов ...)

Например, следующее изменит содержимое файла (используйте sed -i для изменения на месте, он просто печатает на стандартный вывод), чтобы добавить новую строку, если она отсутствует (или оставить файл без изменений, если уже есть новая строка):

sed -e '$a\'  file1.txt

И просто для просмотра синтаксиса 'diff' (возвращение true означает, что они одинаковы, false означает разные):

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

Убедитесь, что только пробелы отличаются:

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

В bash мы можем использовать sed для манипулирования содержимым файла, когда оно передается в diff (исходные файлы остаются без изменений):

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

Теперь все, что вам нужно сделать, это эмулировать diff -r для рекурсивного сравнения каталогов. Если сравнивать каталоги a и b , то для всех файлов в a (например, a/dir1/dir2/file.txt) выведите путь к файлу в b (например, b/dir1/dir2/file.txt) и сравните:

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

Чуть более многословная версия:

$ 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
0

Ответ прост.
Сообщение об отсутствующей новой строке находится не в выходном потоке diff а в потоке ошибок. Так что согни его в нирвану, и вы сделали навсегда

diff -rqEeB fileA fileB 2> /dev/null
0

Я решил проблему, добавив новую строку в каждый из файлов и проигнорировав пустые строки в diff (опция -B). Эти решения могут не подходить для вашего случая использования, но могут помочь другим:

echo >> $FILE1 
echo >> $FILE2
diff -B $FILE1 FILE2 
0

Просто подумал о другом подходе, который будет работать для больших файлов (и при этом не копировать и не изменять исходные файлы). Вам все равно придется эмулировать рекурсивный обход каталога (и есть несколько способов сделать это), но этот пример не использует «sed», а просто сравнивает два файла, исключая последний байт, используя cmp , например ,

$ 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

По-прежнему циклически перебирайте все файлы в каталоге, и для двух файлов a/file.txt и b/file.txt вычислите больший размер файла и вычтите один, а затем выполните двоичный анализ diff (cmp), используя это число байтов (также в баш)

(( 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

Циклы по файлам будут такими же, как в другом ответе с использованием sed и diff .

0

Передайте вывод diff в команду grep которая отбрасывает сообщение, которое вы не хотите видеть.

-1

В diff commnad есть флаг: --strip-trailing-cr который делает именно то, что вы просили

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .