Долго рассуждая о дедупликации, настал момент когда мне потребовалось использовать эту технологию в личных целях, для наведения порядка в домашней инфраструктуре.
Достаточно часто приходится сливать информацию с медиа-носителей на десктоп/лаптоп/файловый сервер в попыхах, помещая ее куда-нибудь, лишь бы сохранить, с иллюзией о том, что скоро руки уж точно дойдут все разобрать в этот раз.
Было принято решение найти дубликаты фотографий и аудио данных, ввиду их большого объема и колосальной избыточности, растущей с каждой неделей.
Так как в моем случае речь идет о поиске дубликатов изображений и музыки, есть возможность (которой я конечно же воспользуюсь) не использовать хеширование ввиду больших расходов на вычисления хешей. Определение дубликата будет происходить на основе имени и размера файла.
Скрипт
Ниже я привожу написанный мной скрипт на bash, позволяющий осуществить дедупликацию по заданным параметрам.
#!/bin/bash
path=$1 #Директория откуда осуществлять поиск
datafile="/tmp/datafile" #Файл для хранения информации о необходимых файлах
find -P $path -type f -iregex '.*\.\(JPG\|JPEG\|RAW\|mp3\|wav\|flac\)' -exec du {} \; | sed 's/\(\/.*\/\)\(.*\)/\2\ \1\2/g' |sed 's/\t/\ /g' >"$datafile"
cat /tmp/datafile |while read line
do
IFS=$(echo) # Добавили разделитель
p=$(echo $line | awk '{print $1" "$2}') #Выбрали из очередной строки размер и имя фала
del="`(grep "$p" /tmp/datafile)`" #Выбрали строки с идентичными файлами
#echo $del|wc -l
if [ $(echo $del|wc -l) -ne "1" ] #Проверка на наличие дубликатов
then
count=0 #Переменная для оставления первого файла
first="" #Переменная для отображения первого файла
# for i in $del #Перебор идентичных файлов
echo $del |while read i
do
if [ $count -ne 0 ] #Для оставления первого файла
then
deleted=$(echo $i |awk '{print $3}')
echo "File $deleted match with $first" #Уведомление о совпадающих файлах
echo $deleted |xargs rm -f #Удаление повторяющегося файла
tmp=$(grep -v $i "$datafile") #Удаление из списка удаленного файла
echo $tmp>$datafile
ln -s $first $deleted #Создание ссылки вместо файла
else
first=$(echo $i|awk '{print $3}') #Запись эталонного (первого) файла
count=1
fi
done
fi
done;
~
Для FreeBSD, ввиду небольших разностей синтаксиса 4 строка примет следующий вид
find -E $path -type f -iregex '.*\.(JPG|JPEG|RAW|mp3|wav|flac)' -exec du {} \; | sed 's/\(\/.*\/\)\(.*\)/\2\ \1\2/g' |sed 's/\t/\ /g' >$datafile
Небольшие пояснения :
$path — место откуда будет осуществлен поиск файлов
Ключ P — не будет переходить по симлинкам
-iregex — позволяет использовать регулярные выражения (префикс i осуществляет поиск без учета регистра символов). В теле регулярного выражения указаны необходимые мне форматы файлов.
Sed используем для преобразования ввода в вид
размер Имя файла Полное имя файла
из вида
размер Полное имя файла
$datafile — файл в который запишется вывод нашей комманды
sed 's/\t/\ /g' — замена табуляции на пробел, для удобной дальнейшей работы.
Соответственно поиск будет осуществляться простым регулярным выражением по первым двум столбцам
P.S. Скрипт можно написать гораздо более оптимально, но я данную задачу не преследовал :-)