Как можно рекурсивно сравнить два каталога и проверить, содержит ли один из них другой?
У меня есть две директории, они содержат общие файлы. Я хочу знать, содержит ли один каталог тот же файл, что и другой. Я нашел скрипт в сети, но мне нужно улучшить его, чтобы он выполнялся рекурсивно.
#!/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 файлы глубоко в дереве несколько раз (один раз на папку предка)
- Не обнаруживает различий в отметках времени файла, владельца или разрешений
- Игнорирует пустые папки. Таким образом, две папки, которые содержат идентичные файлы или не содержат файлов, но содержат разные пустые папки внутри, все равно будут отображаться как идентичные.
- Игнорирует символические ссылки. Файлы, содержащие разные символические ссылки, могут по-прежнему указываться как идентичные.