Как найти дубликаты файлов с одинаковым именем, но в другом случае, которые существуют в одном каталоге в Linux?
Как я могу вернуть список файлов с именами дубликатов, то есть иметь одно и то же имя, но в другом случае, которые существуют в одном каталоге?
Меня не интересует содержимое файлов. Мне просто нужно знать местоположение и имя любых файлов, имеющих дубликат с тем же именем.
Пример дубликатов:
/www/images/taxi.jpg
/www/images/Taxi.jpg
В идеале мне нужно искать все файлы рекурсивно из базового каталога. В приведенном выше примере это было /www/
Ответы
Ответ 1
Другой ответ велик, но вместо "довольно чудовищного" perl script я предлагаю
perl -pe 's!([^/]+)$!lc $1!e'
Что будет содержать строчку только части имени файла пути.
Изменить 1: Фактически вся проблема может быть решена с помощью:
find . | perl -ne 's!([^/]+)$!lc $1!e; print if 1 == $seen{$_}++'
Редактирование 3: Я нашел решение с помощью sed, sort и uniq, которое также распечатает дубликаты, но оно работает только в том случае, если в именах файлов нет пробелов:
find . |sed 's,\(.*\)/\(.*\)$,\1/\2\t\1/\L\2,'|sort|uniq -D -f 1|cut -f 1
Изменить 2: И вот более длинный script, который будет печатать имена, он принимает список путей на stdin, как указано find
. Не так элегантно, но все же:
#!/usr/bin/perl -w
use strict;
use warnings;
my %dup_series_per_dir;
while (<>) {
my ($dir, $file) = m!(.*/)?([^/]+?)$!;
push @{$dup_series_per_dir{$dir||'./'}{lc $file}}, $file;
}
for my $dir (sort keys %dup_series_per_dir) {
my @all_dup_series_in_dir = grep { @{$_} > 1 } values %{$dup_series_per_dir{$dir}};
for my $one_dup_series (@all_dup_series_in_dir) {
print "$dir\{" . join(',', sort @{$one_dup_series}) . "}\n";
}
}
Ответ 2
Try:
ls -1 | tr '[A-Z]' '[a-z]' | sort | uniq -c | grep -v " 1 "
Простой, на самом деле:-) Не являются ли конвейеры замечательными зверями?
ls -1
дает вам файлы по одной в строке, tr '[A-Z]' '[a-z]'
преобразует все прописные буквы в нижний регистр, sort
сортирует их (что удивительно), uniq -c
удаляет последующие вхождения дубликатов строк, давая вам счет а также, наконец, grep -v " 1 "
выделяет те линии, где счетчик был одним.
Когда я запускаю его в директории с одним "дубликатом" (я скопировал qq
в qq
), я получаю:
2 qq
Для версии "этот каталог и каждый подкаталог" просто замените ls -1
на find .
или find DIRNAME
, если вы хотите, чтобы определенная начальная точка каталога (DIRNAME
- это имя каталога, которое вы хотите использовать).
Это возвращает (для меня):
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3/%gconf.xml
2 ./.gnome2/accels/blackjack
2 ./qq
вызванные:
pax> ls -1d .gnome2/accels/[bB]* .gconf/system/gstreamer/0.10/audio/profiles/[mM]* [qQ]?
.gconf/system/gstreamer/0.10/audio/profiles/mp3
.gconf/system/gstreamer/0.10/audio/profiles/MP3
.gnome2/accels/blackjack
.gnome2/accels/Blackjack
qq
qQ
Update:
Собственно, при дальнейшем отражении, tr
будет содержать нижние строчки всех компонентов пути, так что обе
/a/b/c
/a/B/c
будут считаться дублирующими, даже если они находятся в разных каталогах.
Если вы хотите, чтобы дубликаты в одном каталоге отображались как совпадение, вы можете использовать (довольно чудовищный):
perl -ne '
chomp;
@flds = split (/\//);
$lstf = $f[-1];
$lstf =~ tr/A-Z/a-z/;
for ($i =0; $i ne $#flds; $i++) {
print "$f[$i]/";
};
print "$x\n";'
вместо:
tr '[A-Z]' '[a-z]'
То, что он делает, - это только очертить конечную часть пути, а не всего. Кроме того, если вам нужны только обычные файлы (нет каталогов, FIFO и т.д.), Используйте find -type f
, чтобы ограничить возвращаемое.
Ответ 3
Я верю
ls | sort -f | uniq -i -d
проще, быстрее и даст тот же результат
Ответ 4
После ответа на mpez0, чтобы обнаружить рекурсивно, просто замените "ls" на "find.".
Единственная проблема, с которой я вижу, заключается в том, что если это дубликат, то у вас есть 1 запись для каждого файла в этом каталоге. Некоторый мозг человека необходим для обработки результатов этого.
Но так или иначе, вы не автоматически удаляете эти файлы, не так ли?
find . | sort -f | uniq -i -d
Ответ 5
Это приложение с маленькой командной строкой, называемое findsn
, которое вы получаете, если вы скомпилируете fslint
, что пакет deb не включает.
он найдет файлы с тем же именем и его молниеносно, и он может обрабатывать разные случаи.
/findsn --help
find (files) with duplicate or conflicting names.
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...]
Если аргументы не заданы, поиск $PATH выполняется для любого избыточного
или конфликтующие файлы.
-A reports all aliases (soft and hard links) to files.
If no path(s) specified then the $PATH is searched.
Если указан только указанный путь (ы), то они проверяются на дубликат имени
файлы. Вы можете квалифицировать это с помощью -C, чтобы игнорировать случай в этом поиске.
Квалификация с помощью -c более ограничительна, поскольку только файлы (или каталоги)
в том же каталоге, имена которых отличаются только в том случае, если они сообщаются.
И.Е. -c будет отмечать файлы и каталоги, которые будут конфликтовать при передаче
к файловой системе, не учитывающей регистр. Обратите внимание, если указано -c или -C и
не указан путь (ы), указанный текущий каталог предполагается.
Ответ 6
Вот пример, как найти все дубликаты файлов jar:
find . -type f -printf "%f\n" -name "*.jar" | sort -f | uniq -i -d
Замените *.jar
на любой повторяющийся тип файла, который вы ищете.
Ответ 7
Здесь script, который работал на меня (я не автор). оригинал и обсуждение можно найти здесь:
http://www.daemonforums.org/showthread.php?t=4661
#! /bin/sh
# find duplicated files in directory tree
# comparing by file NAME, SIZE or MD5 checksum
# --------------------------------------------
# LICENSE(s): BSD / CDDL
# --------------------------------------------
# vermaden [AT] interia [DOT] pl
# http://strony.toya.net.pl/~vermaden/links.htm
__usage() {
echo "usage: $( basename ${0} ) OPTION DIRECTORY"
echo " OPTIONS: -n check by name (fast)"
echo " -s check by size (medium)"
echo " -m check by md5 (slow)"
echo " -N same as '-n' but with delete instructions printed"
echo " -S same as '-s' but with delete instructions printed"
echo " -M same as '-m' but with delete instructions printed"
echo " EXAMPLE: $( basename ${0} ) -s /mnt"
exit 1
}
__prefix() {
case $( id -u ) in
(0) PREFIX="rm -rf" ;;
(*) case $( uname ) in
(SunOS) PREFIX="pfexec rm -rf" ;;
(*) PREFIX="sudo rm -rf" ;;
esac
;;
esac
}
__crossplatform() {
case $( uname ) in
(FreeBSD)
MD5="md5 -r"
STAT="stat -f %z"
;;
(Linux)
MD5="md5sum"
STAT="stat -c %s"
;;
(SunOS)
echo "INFO: supported systems: FreeBSD Linux"
echo
echo "Porting to Solaris/OpenSolaris"
echo " -- provide values for MD5/STAT in '$( basename ${0} ):__crossplatform()'"
echo " -- use digest(1) instead for md5 sum calculation"
echo " $ digest -a md5 file"
echo " -- pfexec(1) is already used in '$( basename ${0} ):__prefix()'"
echo
exit 1
(*)
echo "INFO: supported systems: FreeBSD Linux"
exit 1
;;
esac
}
__md5() {
__crossplatform
:> ${DUPLICATES_FILE}
DATA=$( find "${1}" -type f -exec ${MD5} {} ';' | sort -n )
echo "${DATA}" \
| awk '{print $1}' \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SUM=$( echo ${LINE} | awk '{print $2}' )
echo "${DATA}" | grep ${SUM} >> ${DUPLICATES_FILE}
done
echo "${DATA}" \
| awk '{print $1}' \
| sort -n \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SUM=$( echo ${LINE} | awk '{print $2}' )
echo "count: ${COUNT} | md5: ${SUM}"
grep ${SUM} ${DUPLICATES_FILE} \
| cut -d ' ' -f 2-10000 2> /dev/null \
| while read LINE
do
if [ -n "${PREFIX}" ]
then
echo " ${PREFIX} \"${LINE}\""
else
echo " ${LINE}"
fi
done
echo
done
rm -rf ${DUPLICATES_FILE}
}
__size() {
__crossplatform
find "${1}" -type f -exec ${STAT} {} ';' \
| sort -n \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SIZE=$( echo ${LINE} | awk '{print $2}' )
SIZE_KB=$( echo ${SIZE} / 1024 | bc )
echo "count: ${COUNT} | size: ${SIZE_KB}KB (${SIZE} bytes)"
if [ -n "${PREFIX}" ]
then
find ${1} -type f -size ${SIZE}c -exec echo " ${PREFIX} \"{}\"" ';'
else
# find ${1} -type f -size ${SIZE}c -exec echo " {} " ';' -exec du -h " {}" ';'
find ${1} -type f -size ${SIZE}c -exec echo " {} " ';'
fi
echo
done
}
__file() {
__crossplatform
find "${1}" -type f \
| xargs -n 1 basename 2> /dev/null \
| tr '[A-Z]' '[a-z]' \
| sort -n \
| uniq -c \
| sort -n -r \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && break
FILE=$( echo ${LINE} | cut -d ' ' -f 2-10000 2> /dev/null )
echo "count: ${COUNT} | file: ${FILE}"
FILE=$( echo ${FILE} | sed -e s/'\['/'\\\['/g -e s/'\]'/'\\\]'/g )
if [ -n "${PREFIX}" ]
then
find ${1} -iname "${FILE}" -exec echo " ${PREFIX} \"{}\"" ';'
else
find ${1} -iname "${FILE}" -exec echo " {}" ';'
fi
echo
done
}
# main()
[ ${#} -ne 2 ] && __usage
[ ! -d "${2}" ] && __usage
DUPLICATES_FILE="/tmp/$( basename ${0} )_DUPLICATES_FILE.tmp"
case ${1} in
(-n) __file "${2}" ;;
(-m) __md5 "${2}" ;;
(-s) __size "${2}" ;;
(-N) __prefix; __file "${2}" ;;
(-M) __prefix; __md5 "${2}" ;;
(-S) __prefix; __size "${2}" ;;
(*) __usage ;;
esac
Если команда find не работает для вас, возможно, вам придется ее изменить. Например
OLD : find "${1}" -type f | xargs -n 1 basename
NEW : find "${1}" -type f -printf "%f\n"
Ответ 8
Вы можете использовать:
find -type f -exec readlink -m {} \; | gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}' | uniq -c
Где:
-
find -type f
рекурсия распечатать весь файл полный путь.
-
-exec readlink -m {} \;
получить абсолютный путь файла
-
gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'
замените все имена файлов на нижний регистр
-
uniq -c
уникальный путь, -c вывести счетчик дубликатов.
Ответ 9
Немного допоздна к этому, но вот версия, с которой я пошел:
find . -type f | awk -F/ '{print $NF}' | sort -f | uniq -i -d
Здесь мы используем:
-
find
- найти все файлы под текущим dir
-
awk
- удалить часть пути к файлу имени файла
-
sort
- неучетно сортировать регистр
-
uniq
- найдите дубликатов из того, что делает это через трубку
(Вдохновленный ответом @mpez0, и @SimonDowdles комментируют ответ @paxdiablo.)
Ответ 10
Вы можете проверить дубликаты в заданном каталоге с помощью GNU awk:
gawk 'BEGINFILE {if ((seen[tolower(FILENAME)]++)) print FILENAME; nextfile}' *
Здесь используется BEGINFILE для выполнения некоторых действий перед продолжением и чтением файла. В этом случае он отслеживает имена, появившиеся в массиве seen[]
, индексы которого являются именами файлов в нижнем регистре.
Если имя уже появилось, независимо от его случая, оно печатает его. В противном случае он просто переходит к следующему файлу.
См. пример:
$ tree
.
├── bye.txt
├── hello.txt
├── helLo.txt
├── yeah.txt
└── YEAH.txt
0 directories, 5 files
$ gawk 'BEGINFILE {if ((a[tolower(FILENAME)]++)) print FILENAME; nextfile}' *
helLo.txt
YEAH.txt
Ответ 11
Я просто использовал fdupes для CentOS для очистки всего дубликата файлов buncha...
yum install fdupes