Как можно рекурсивно сравнить два каталога и проверить, содержит ли один из них другой?

У меня есть две директории, они содержат общие файлы. Я хочу знать, содержит ли один каталог тот же файл, что и другой. Я нашел скрипт в сети, но мне нужно улучшить его, чтобы он выполнялся рекурсивно.

  #!/bin/bash

  # cmp_dir - program to compare two directories

  # Check for required arguments
  if [ $# -ne 2 ]; then
      echo "usage: $0 directory_1 directory_2" 1>&2
      exit 1
  fi

  # Make sure both arguments are directories
  if [ ! -d $1 ]; then
      echo "$1 is not a directory!" 1>&2
      exit 1
  fi

  if [ ! -d $2 ]; then
      echo "$2 is not a directory!" 1>&2
      exit 1
  fi

  # Process each file in directory_1, comparing it to directory_2
  missing=0
  for filename in $1/*; do
      fn=$(basename "$filename")
      if [ -f "$filename" ]; then
          if [ ! -f "$2/$fn" ]; then
              echo "$fn is missing from $2"
              missing=$((missing + 1))
          fi
      fi
  done
  echo "$missing files missing"

Кто-нибудь предложил бы алгоритм для этого?

3 ответа

Решение
#!/bin/bash

# cmp_dir - program to compare two directories

# Check for required arguments
if [ $# -ne 2 ]; then
  echo "usage: $0 directory_1 directory_2" 1>&2
  exit 1
fi

# Make sure both arguments are directories
if [ ! -d "$1" ]; then
  echo "$1 is not a directory!" 1>&2
  exit 1
fi

if [ ! -d "$2" ]; then
  echo "$2 is not a directory!" 1>&2
  exit 1
fi

# Process each file in directory_1, comparing it to directory_2
missing=0
while IFS= read -r -d $'\0' filename
do
  fn=${filename#$1}
  if [ ! -f "$2/$fn" ]; then
      echo "$fn is missing from $2"
      missing=$((missing + 1))
  fi
done < <(find "$1" -type f -print0)

echo "$missing files missing"

Обратите внимание, что я добавил двойные кавычки $1 а также $2 в различных местах выше, чтобы защитить их расширение оболочки. Без двойных кавычек имена каталогов с пробелами или другими сложными символами могут привести к ошибкам.

Ключевой цикл теперь гласит:

while IFS= read -r -d $'\0' filename
do
  fn=${filename#$1}
  if [ ! -f "$2/$fn" ]; then
      echo "$fn is missing from $2"
      missing=$((missing + 1))
  fi
done < <(find "$1" -type f -print0)

Это использует find рекурсивно погрузиться в каталог $1 и найти имена файлов. Конструкция while IFS= read -r -d $'\0' filename; do .... done < <(find "$1" -type f -print0) безопасен против всех имен файлов.

basename больше не используется, потому что мы смотрим на файлы в подкаталогах, и нам нужно сохранить подкаталоги. Таким образом, вместо вызова basename, линия fn=${filename#$1} используется. Это просто удаляет из filename префикс, содержащий каталог $1,

Проблема 2

Предположим, что мы сопоставляем файлы по имени, но независимо от каталога. Другими словами, если первый каталог содержит файл a/b/c/some.txtБудем считать, что он присутствует во втором каталоге, если файл some.txt существует в любом подкаталоге второго каталога. Для этого замените цикл выше на:

while IFS= read -r -d $'\0' filename
do
  fn=$(basename "$filename")
  if ! find "$2" -name "$fn" | grep -q . ; then
      echo "$fn is missing from $2"
      missing=$((missing + 1))
  fi
done < <(find "$1" -type f -print0)

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

Установка FSlint

Установите FSlint из Ubuntu Software Center или из командной строки следующим образом:

sudo apt-get install fslint

(В моей системе установка FSlint не требовала дополнительных зависимостей. В частности, fslint зависит от findutils, python и python-glade2, которые уже должны быть в вашей системе. Вы можете удалить FSlint с помощью Центра программного обеспечения или набрав sudo apt-get autoremove --purge fslint в терминале).

Поиск файлов

Запустите FSlint из Unity Dash.

Вот скриншот основного экрана. Существует множество расширенных функций, но базовое использование приложения относительно просто.

Нажмите на Add Кнопка в левом верхнем углу, чтобы добавить все каталоги, которые вы хотели бы проверить. Очевидно, что вы можете удалить каталоги, используя Remove кнопка.

введите описание здесь

Убедитесь, что recurse? флажок справа установлен. Затем нажмите Find кнопка. (Любые ошибки, такие как проблемы с правами доступа к файлам, будут напечатаны в нижней части окна FSlint).

FSlint перечислит все дубликаты файлов, их каталоги и дату файла. FSlint также представляет вам количество байтов, потраченных впустую из-за избыточных файлов.

Удаление Дубликатов

Теперь вы можете выбрать несколько файлов, используя клавиши Shift или Ctrl и левую кнопку мыши. Если вы хотите выбрать несколько файлов автоматически, нажмите на Select кнопку, и вам будут предоставлены варианты, такие как выбор файлов по дате или ввод критериев выбора подстановочных знаков.

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

Наконец, вы можете удалить выбранные файлы, используя Delete или вы можете объединить выбранные файлы, используя Merge кнопка. Обратите внимание, что Merge Функция удаляет невыбранные файлы из вашей системы и создает жесткие ссылки на соответствующие выбранные файлы. Вы бы использовали эту функцию, если хотите сохранить существующую файловую структуру, но хотите освободить место в вашей системе.

введите описание здесь

Дополнительные функции и документация

FSlint имеет другие мощные функции, которые доступны на вкладках в левой панели. я нашел Name clashes быть полезным, когда есть файлы с одинаковыми именами, но разные (возможно, потому что вы сохранили более новую версию файла в другом каталоге).

Также есть Advanced search parameters вкладка в верхней части окна FSlint, которая позволяет исключить определенные каталоги из вашего поиска или отфильтровать результаты с помощью параметров.

В этом простом маленьком инструменте много мощных функций. Это может избавить вас от необходимости писать и отлаживать скрипт. Вы можете узнать больше на http://www.pixelbeat.org/fslint/. Вот прямая ссылка на руководство на английском языке: http://en.flossmanuals.net/fslint/.

Я хочу поделиться этим, потому что я думаю, что это довольно весело.

Для каждой подпапки ниже целевой папки мы генерируем хеш для этой подпапки.

Хэш каждой папки генерируется в результате хеширования всех файлов ниже этой папки. Поэтому любая папка, содержащая идентичные файлы в одной и той же структуре, должна создавать одинаковый хэш!

В конце мы используем uniq отображать только дубликаты папок хэшей.

Сохраните следующий скрипт как seek_duplicate_folders.sh а затем запустите его так:

$ bash seek_duplicate_folders.sh [root_folder_to_scan]

Вот сценарий:

#!/bin/bash
target="$1"

hash_folder() {
  echo "Hashing $1" >/dev/stderr
  pushd "$1" >/dev/null

  # Hash all the files
  find . -type f | sort | xargs md5sum |

  # Hash that list of hashes, discard the newline character,
  # and append the folder name
  md5sum - | tr -d '\n'
  printf "  %s\n" "$1"

  popd >/dev/null
}

find "$target" -type d |
while read dir
do hash_folder "$dir"
done |
sort |
# Display only the lines with duplicate hashes (first 32 chars are duplicates)
uniq -D -w 32

Предостережения:

  • Неэффективно: это md5sums файлы глубоко в дереве несколько раз (один раз на папку предка)
  • Не обнаруживает различий в отметках времени файла, владельца или разрешений
  • Игнорирует пустые папки. Таким образом, две папки, которые содержат идентичные файлы или не содержат файлов, но содержат разные пустые папки внутри, все равно будут отображаться как идентичные.
  • Игнорирует символические ссылки. Файлы, содержащие разные символические ссылки, могут по-прежнему указываться как идентичные.
Другие вопросы по тегам