Как обнаружить только разные файлы в моем сценарии оболочки 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

Проблемы:

  1. У вас была опечатка new/$fullfile скорее, чем new/$filename
  2. Вы должны использовать "new/$filename" (т.е. используйте двойные кавычки) вокруг расширений имени файла
  3. использование 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)

Другие вопросы по тегам