Подтвердить что ты не робот

Как найти дубликаты файлов с одинаковым именем, но в другом случае, которые существуют в одном каталоге в Linux?

Как я могу вернуть список файлов с именами дубликатов, то есть иметь одно и то же имя, но в другом случае, которые существуют в одном каталоге?

Меня не интересует содержимое файлов. Мне просто нужно знать местоположение и имя любых файлов, имеющих дубликат с тем же именем.

Пример дубликатов:

/www/images/taxi.jpg
/www/images/Taxi.jpg

В идеале мне нужно искать все файлы рекурсивно из базового каталога. В приведенном выше примере это было /www/

4b9b3361

Ответ 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