Рекурсивно сравнивать две директории с diff -r без вывода неработающих ссылок

38

Я использую diff -r a bдля рекурсивного сравнения каталогов a и b . Однако часто случается, что существуют неработающие ссылки (одни и те же неработающие ссылки в каталогах a и b и указывающие на одни и те же несуществующие цели).

Затем diff выводит сообщения об ошибках для этих случаев и завершает работу с ненулевым кодом выхода, однако я хотел бы, чтобы он молчал и выходил с 0, поскольку каталоги в моей книге совпадают.

Как я могу это сделать?

Маркус Юний Брут
источник
Вы все еще хотите, чтобы символические ссылки сравнивались (и идентифицировались как эквивалентные, но неработающие), или допустимо игнорировать все символические ссылки при выполнении этого сравнения?
ire_and_curses
сравнивать и идентифицировать как эквивалент, мне все равно, если они сломаны. Я просто пытаюсь убедиться, что мой rsync сработал.
Маркус Юний Брут

Ответы:

24

Для версии 3.3 или новее diffвы должны использовать эту --no-dereferenceопцию, как описано в ответе Пита Харлана .

К сожалению, старые версии diff не поддерживают игнорирование символических ссылок :

Некоторые файлы не являются ни каталогами, ни обычными файлами: это необычные файлы, такие как символические ссылки, специальные файлы устройств, именованные каналы и сокеты. В настоящее время diffобрабатывает символические ссылки как обычные файлы; он обрабатывает другие специальные файлы как обычные файлы, если они указаны на верхнем уровне, но просто сообщает об их наличии при сравнении каталогов. Это означает, что patchне может представлять изменения в таких файлах. Например, если вы изменяете, на какой файл указывает символическая ссылка, diffвыводится разница между двумя файлами вместо изменения символической ссылки.

diffпри необходимости должен специально сообщать об изменениях в специальные файлы и patchдолжен быть расширен для понимания этих расширений.

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

Если вы действительно хотите это сделать diff, то можете findпропустить символические ссылки и запустить diff для каждого файла в отдельности. Передайте ваши каталоги a и b в качестве аргументов:

#!/bin/bash
# Skip files in $1 which are symlinks
for f in `find $1/* ! -type l`
do
    # Suppress details of differences
    diff -rq $f $2/${f##*/}
done

или как однострочник:

for f in `find a/* ! -type l`;do diff -rq $f b/${f##*/};done

Это идентифицирует файлы, которые отличаются по содержанию, или файлы, которые находятся в a, но не в b .

Обратите внимание, что:

  • так как мы пропускаем символические ссылки полностью, это не заметит, если имена символических ссылок отсутствуют в b . Если вам необходимо, вам понадобится второй проход поиска, чтобы идентифицировать все символические ссылки, а затем явно проверить их существование в b .
  • Дополнительные файлы в b не будут идентифицированы, так как список составлен из содержимого a . Это, вероятно, не проблема для вашего rsyncсценария.
ire_and_curses
источник
Предложенный сценарий не работает рекурсивно для любых каталогов, присутствующих в каталоге 'a' (пути, созданные для 'b' с использованием b / $ {f ## *}, не верны).
Маркус Юний Брут
@MarcusJuniusBrutus - Да, ты прав. Я думаю, что решение состоит в том, чтобы удалить #, например, for f in найти / *! Тип л ;do echo $f b/${f#*/};done. У меня нет времени, чтобы проверить это прямо сейчас. Дай мне знать, если это работает.
ire_and_curses
Это является , однако лучше все - таки портит путей файлов во многих случаях. Сценарий (с удаленным символом #), по-видимому, необходимо вызывать из каталога непосредственно через «a» для работы.
Маркус Юниус Брутус
Этот ответ устареет при использовании GNU diff 3.3 (см. Сообщения ниже)
Bernd Gloss
В приведенном выше сценарии есть несколько проблем: сначала нужно найти все имена файлов и передать их в расширенную командную строку. (1) Он будет работать только с небольшими коллекциями файлов, так как он. (2) Любое имя файла со специальным символом (даже пробел) не будет обработано. (3) Всегда используйте $(xxx)вместо обратной галочки. Симметрия обратных кавычек делает их менее читаемыми и предотвращает вложение. Что касается 1 и 2 см stackoverflow.com/questions/11366184/...
Стефан Горичон
19

Начиная с версии 3.3 GNU diffподдерживает не разыменование символических ссылок, но затем сравнивает пути, на которые они указывают.

Установите GNU diffutils> = 3.3 и используйте --no-dereferenceопцию; нет короткого варианта для этого.

Диагностика будет молчать, если равна или:

Символические ссылки /tmp/noderef/a/symlinkи /tmp/noderef/b/symlinkотличаются

Филипп де Мюйтер
источник
Теперь, если только он покажет изменения содержимого, как если бы символическая ссылка была обычным файлом ...: - /
lindes
6

Вы можете использовать более новую версию diff

В diffGNU diffutils3.3 есть --no-dereferenceопция, которая позволяет сравнивать сами символические ссылки, а не их цели. Он сообщает, если они различаются, тихо, если они согласны, и ему все равно, сломаны ли они.

Я не знаю, когда была добавлена ​​опция; его нет в 2.8.1.

Пит Харлан
источник
Я могу подтвердить, что в diff (GNU diffutils) 3.2 тоже нет
Elder Geek