Как обнаружить только разные файлы в моем сценарии оболочки bash?
Я пытаюсь сравнить список файлов в двух репозиториях, чтобы определить, какие из них изменились. Проблема в том, что мой код говорит, что все они разные. Но проверка каждого хеш-дайджеста показывает, что многие дайджесты идентичны.
while IFS= read -r filename;
do
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# inspecting the digest of each file individually #
# shows many files are identical and so are the digests #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
md5 old/$filename; # a456cca87913a4788d980ba4c2f254be
md5 new/$filename; # a456cca87913a4788d980ba4c2f254be
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# the below conditional is only supposed to echo "differs" #
# if the two digests are different #
# but, instead, it echoes "differs" on every file comparison #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[[ $(md5 old/$filename) = $(md5 new/$filename) ]] || echo differs; # differs
done < files-to-compare.txt
Как я могу исправить эту ошибку и получить только файлы, которые отличаются для отчета?
редактировать
Также обратите внимание на использование ==
вместо =
как в
$(md5 old/$filename) == $(md5 new/$filename) ]] || echo differs;
дает точно такой же ошибочный вывод.
Edit2
Комментарий предлагает использовать кавычки. Это также не работает.
"$(md5 old/$filename)" == "$(md5 new/$filename)" ]] || echo differs;
4 ответа
Вот ваш сценарий исправлен:
while IFS= read -r filename;
do
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# inspecting the digest of each file individually #
# shows many files are identical and so are the digests #
# It also prints MD5 (full file path) = md5_signature! #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
md5 "old/$filename" # please use double quotes
md5 "new/$filename"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Using -q eliminates all output from md5 except the sig #
# Your script now works correctly #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[[ $(md5 -q "old/$filename") == $(md5 -q "new/$filename") ]] || echo differs; # differs
done < files.txt
Проблемы:
- У вас была опечатка
new/$fullfile
скорее, чемnew/$filename
- Вы должны использовать
"new/$filename"
(т.е. используйте двойные кавычки) вокруг расширений имени файла - использование
md5 -q
сравнить выводmd5
на разных файлах. Иначеmd5
по умолчанию печатает путь к входному файлу в видеMD5 (full_path/base_name) = 2504fcc0c0a57d14aa6b4193b5efaf94
, Поскольку эти пути гарантированно будут разными в двух разных каталогах, разные имена путей приведут к ошибке при сравнении строк.
Комментарии выше предполагают, что вы используете md5
на BSD или, скорее всего, на macOS.
Вот альтернативное решение, которое работает как на Linux с md5sum
и BSD с md5
, Просто передайте содержимое файла на стандартный вывод любой из программ, и будет напечатана только подпись md5:
$ md5 <new/file.pdf
2504fcc0c0a57d14aa6b4193b5efaf94
vs если вы используете имя файла, путь печатается и печатается используемая хеш-подпись MD5:
$ md5 new/file.pdf
MD5 (new/file.pdf) = 2504fcc0c0a57d14aa6b4193b5efaf94
То же самое относится и к md5sum
в основных утилитах Linux или GNU.
Вместо вычисления контрольных сумм MD5, вы можете использовать diff
команда, которая сравнивает содержимое файла. Его основное назначение - построчно обрабатывать файлы и сравнивать их различия (и создавать исправления), но его также легко можно использовать для этой цели. Возвращает выход 0
если нет различий между двумя файлами и 1
если есть какие-то различия
while IFS= read -r filename;
do
if ! diff "old/$filename" "new/$filename" > /dev/null;
then
echo "“$filename” differs"
fi
done < files-to-compare.txt
Если вы используете GNU diff
Вы могли бы просто использовать его -q, --brief
опция, которая сообщает только о том, что файлы различаются (вместо того, чтобы указывать, как они различаются)
while IFS= read -r filename;
do
diff -q "old/$filename" "new/$filename"
done < files-to-compare.txt
На моем Linux Ubuntu, есть md5sum
Команда: она печатает дайджест и имя файла:
md5sum myFile
215e0f7b4ea9fd9ea5f31106155839fe myFile
Я имею в виду, вам нужно извлечь только вывод из вывода:
md5sum myFile | sed 's/^\([^[:blank:]]*\).*$/\1/g'
215e0f7b4ea9fd9ea5f31106155839fe
Затем используйте эту последнюю командную строку в тесте:
...
[[ $(md5sum old/"${filename}" | sed 's/^\([^[:blank:]]*\).*$/\1/g') = $(md5sum new/"${filename}" | sed 's/^\([^[:blank:]]*\).*$/\1/g') ]] || echo differs;
...
Чтобы просмотреть только разницу двух файлов, вы можете использовать grep, и он будет печатать только разные строки.
grep -v -F -x -f filename1 filename2
Также для этой цели можно использовать comm, чтобы распечатать только разницу между двумя файлами.
comm -13 <(sort filename1) <(sort filename2)