Md5 все файлы в дереве каталогов

У меня есть каталог со структурой вроде:

.
├── Test.txt
├── Test1
│   ├── Test1.txt
│   ├── Test1_copy.txt
│   └── Test1a
│       ├── Test1a.txt
│       └── Test1a_copy.txt
└── Test2
   ├── Test2.txt
   ├── Test2_copy.txt
   └── Test2a
       ├── Test2a.txt
       └── Test2a_copy.txt

Я хотел бы создать bash script, который делает контрольную сумму md5 для каждого файла в этом каталоге. Я хочу, чтобы иметь возможность вводить имя script в CLI, а затем путь к каталогу, который я хочу использовать, и заставить его работать. Я уверен, что есть много способов сделать это. В настоящее время у меня есть:

#!/bin/bash

for file in "$1" ; do 
    md5 >> "${1}__checksums.md5"
done

Это просто зависает и не работает. Возможно, я должен использовать find?

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

Ответ 1

Использование md5deep

md5deep -r path/to/dir > sums.md5

Используя find и md5sum

find relative/path/to/dir -type f -exec md5sum {} + > sums.md5

Имейте в виду, что при запуске проверки ваших сумм MD5 с помощью md5sum -c sums.md5 вам нужно запустить его из того же каталога, из которого вы создали файл sums.md5. Это связано с тем, что find выводит пути, относящиеся к вашему текущему местоположению, которые затем помещаются в файл sums.md5.

Если это проблема, вы можете сделать абсолютное значение relative/path/to/dir (например, поставив $PWD/ перед вашим путем). Таким образом, вы можете запустить проверку sums.md5 из любого места. Недостатком является то, что теперь sums.md5 содержит абсолютные пути, что делает его больше.

Полнофункциональная функция с использованием find и md5sum

Вы можете поместить эту функцию в свой .bashrc файл (расположенный в каталоге $HOME):

function md5sums {
  if [ "$#" -lt 1 ]; then
    echo -e "At least one parameter is expected\n" \
            "Usage: md5sums [OPTIONS] dir"
  else
    local OUTPUT="checksums.md5"
    local CHECK=false
    local MD5SUM_OPTIONS=""

    while [[ $# > 1 ]]; do
      local key="$1"
      case $key in
        -c|--check)
          CHECK=true
          ;;
        -o|--output)
          OUTPUT=$2
          shift
          ;;
        *)
          MD5SUM_OPTIONS="$MD5SUM_OPTIONS $1"
          ;;
      esac
      shift
    done
    local DIR=$1 

    if [ -d "$DIR" ]; then  # if $DIR directory exists
      cd $DIR  # change to $DIR directory
      if [ "$CHECK" = true ]; then  # if -c or --check option specified
        md5sum --check $MD5SUM_OPTIONS $OUTPUT  # check MD5 sums in $OUTPUT file
      else                          # else
        find . -type f ! -name "$OUTPUT" -exec md5sum $MD5SUM_OPTIONS {} + > $OUTPUT  # Calculate MD5 sums for files in current directory and subdirectories excluding $OUTPUT file and save result in $OUTPUT file
      fi
      cd - > /dev/null  # change to previous directory
    else
      cd $DIR  # if $DIR doesn't exists, change to it to generate localized error message
    fi
  fi
}

После запуска source ~/.bashrc вы можете использовать md5sums как обычная команда:

md5sums path/to/dir

будет генерировать checksums.md5 файл в каталоге path/to/dir, содержащий MD5 суммы всех файлов в этом каталоге и подкаталогах. Использование:

md5sums -c path/to/dir

для проверки сумм из файла path/to/dir/checksums.md5.

Обратите внимание, что path/to/dir может быть относительным или абсолютным, md5sums будет работать в любом случае. Результирующий файл checksums.md5 всегда содержит пути относительно path/to/dir. Вы можете использовать другое имя файла, а затем по умолчанию checksums.md5, указав опцию -o или --output. Все параметры, кроме -c, --check, -o и --output, передаются в md5sum.

Первая часть определения функции md5sums отвечает за параметры разбора. Подробнее см. этот ответ. Вторая половина содержит пояснительные комментарии.

Ответ 2

Как насчет:

find /path/you/need -type f -exec md5sum {} \; > checksums.md5

Обновление # 1: Улучшена команда, основанная на рекомендации @twalberg, для обработки пробелов в именах файлов.

Обновление # 2: Улучшено на основе предложения @jil, чтобы удалить ненужный вызов xargs и использовать вместо него -exec.

Обновление # 3: @Выполните наивную реализацию вашего script, чтобы выглядеть примерно так:

#!/bin/bash
# Usage: checksumchecker.sh <path>
find "$1" -type f -exec md5sum {} \; > "$1"__checksums.md5

Ответ 3

#!/bin/bash
shopt -s globstar
md5sum "$1"/** > "${1}__checksums.md5"

Объяснение: shopt -s globstar (руководство) разрешает ** рекурсивный глобальный шаблон. Это означает, что "$1"/** будет расширяться до списка всех файлов, рекурсивных в каталоге, заданном как параметр $1. Затем script просто вызывает md5sum с этим списком файлов в качестве параметра и > "${1}__checksums.md5" перенаправляет вывод в файл.

Ответ 4

Обновленный ответ

Если вам нравится ответ ниже или любой другой, вы можете сделать функцию, которая выполняет эту команду для вас. Итак, чтобы протестировать его, введите в Terminal следующее:

function sumthem(){ find "$1" -type f -print0 | parallel -0 -X md5 > checksums.md5; }

Тогда вы можете просто использовать:

sumthem /Users/somebody/somewhere

Если это работает так, вы можете добавить эту строку в конец своего "bash профиля", и функция будет объявлена ​​и доступна всякий раз, когда вы войдете в систему. Ваш "bash профиль", вероятно, находится в $HOME/.profile

Оригинальный ответ

Почему бы вам не запустить все ваши ядра процессора параллельно?

find . -type f -print0 | parallel -0 -X md5sum

Это находит все файлы (-type f) в текущем каталоге (.) и печатает их с нулевым байтом в конце. Затем они передаются в GNU Parallel, которому сообщается, что имена файлов заканчиваются нулевым байтом (-0) и что он должен делать как можно больше файлов за раз (-X) для сохранения создания нового процесса для каждого файла, и он должен md5sum файлы.

Этот подход будет платить самый большой бонус, с точки зрения скорости, с большими изображениями, такими как файлы Photoshop.

Ответ 5

md5deep -r $your_directory | awk {'print $1'} | sort | md5sum | awk {'print $1'}