Запрещает diff проверять наличие новой строки в конце файла

21

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

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

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

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

dangonfast
источник

Ответы:

17

В основном вам нужно сравнить два файла, условно игнорируя завершающий байт. Для этого нет опции '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
Майкл
источник
не могли бы вы объяснить, что sed -e '$a\'именно? thx
törzsmókus
запустите sed, используя приведенный ниже -eскрипт / выражение ( ), соответствующий концу file ( $), и выполните действие «добавить» (a \), но на самом деле не указывайте никакого текста (ничего после `\`), который все еще собирается добавить EOF / newline в конец файла (только если он отсутствует).
Майкл
Спасибо. Я еще не видел a\ .
törzsmókus
1

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

echo >> $FILE1 
echo >> $FILE2
diff -B $FILE1 FILE2 
Jakob
источник
0

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

Дэвид Шварц
источник
не хорошо. diff -r существует с результатом! = 0, если я не добавлю --ignore-all-space. Чтобы было ясно: я хочу, чтобы diff игнорировал переводы строк в EOF и только в EOF. И я хочу, чтобы он сообщил о результате, который соответствует этим критериям. То есть, если файлы в дереве отличаются только на новой строке в EOF, это не должно рассматриваться как различие, и поэтому diff должно возвращать 0.
dangonfast
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а в потоке ошибок. Так что согни его в нирвану, и вы сделали навсегда

diff -rqEeB fileA fileB 2> /dev/null
yunzen
источник
diff возвращает значение! = 0, если находит различия, и я хочу проверить это значение. Перенаправление в / dev / null не заставляет diff забыть об этой разнице, поэтому возвращаемое значение равно! = 0, что мне не нужно. Я хочу, чтобы diff считал два файла равными, если единственным отличием является последняя
новая строка
-1

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

дхарман
источник
-1. Вы пробовали это? Он обрабатывается /r/nкак /nи не имеет ничего общего с дополнительным /nтолько перед EOF.
Камиль Мачоровский
Я пробовал это, и использовал его для сравнения файлов с различными DOS / Unix Newline ... не так ли?
Дхарман
Вопрос в том, чтобы игнорировать перевод строки только в EOF (конец файла).
Камиль Мачоровски