Скрипт для поиска всех файлов с одинаковыми именами (отличается только регистром?) - PullRequest
3 голосов
/ 20 июня 2011

Я работал над репозиторием SVN, используя только командную строку.Теперь мне нужно привлечь пользователей, которым требуется графический интерфейс для взаимодействия с репозиторием, однако это создает ряд проблем с файлами с одинаковыми именами.

Так получилось, что большое количество изображений было продублировано по причинам, связанным с отсутствием связи или лени.

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

Я не знаюНе против придумать сценарий Perl, чтобы справиться с этим сам, однако мне интересно, если такая вещь уже существует или у кого-нибудь есть какие-либо советы, прежде чем я засучу рукава?

Спасибо: D

Ответы [ 7 ]

3 голосов
/ 20 июня 2011

Я полагаюсь на md5sum для этого типа проблемы:

find * -type f | xargs md5sum | sort | uniq -Dw32

Если вы используете svn, вы захотите исключить ваши каталоги .svn.Это распечатает все файлы с их путями, которые имеют идентичное содержимое.

Если вы действительно хотите сопоставлять только файлы, отличающиеся регистром, вы можете добавить еще несколько вещей в приведенный выше конвейер:

find * -type f  | xargs md5sum | sort | uniq -Dw32 | awk -F'[ /]' '{ print $NF }' | sort -f | uniq -Di
myimage_23.png
MyImage_23.png
1 голос
/ 20 июня 2011

Предлагаю попробовать fdupes или duff

1 голос
/ 20 июня 2011

Я думаю, это будет что-то вроде:

#!perl
use File::Spec;
sub check_dir {
    my ($dir, $out) = @_;
    $out ||= [];
    opendir DIR, $dir or die "Impossible to read dir: $!";
    my @files = sort grep { /^[^\.]/ } readdir(DIR); # Ignore files starting with dot
    closedir DIR;
    my @nd = map { ! -d $_ ? File::Spec->catfile($dir, $_) : () } @files;
    for my $i (0 .. $#nd-1){
        push @$out, $nd[$i]
            if lc $nd[$i] eq lc $nd[$i+1]
            and -s $nd[$i] == -s $nd[$i+1];
    }
    map { -d $_ ? &check_dir($_, $out) : () } @files;
    return $out;
}
print join "\n", @{&check_dir(shift @ARGV)}, "";

Пожалуйста, проверьте перед использованием, у меня нет доступа к машинам с Windows (это не происходит в Un * x).Также обратите внимание, что в случае двух файлов с одинаковыми именами (кроме случая) и одинакового размера будет напечатан только первый.В случае трех только первые два и т. Д. (Разумеется, вам нужно будет сохранить один!).

1 голос
/ 20 июня 2011

Насколько я знаю, то, что вы хотите, не существует как таковое. Тем не менее, вот реализация в bash:

#!/bin/bash

dir=("$@")
matched=()
files=()

lc(){ tr '[:upper:]' '[:lower:]' <<< ${*} ; }

in_list() {
    local search="$1"
    shift
    local list=("$@")
    for file in "${list[@]}" ; do
        [[ $file == $search ]] && return 0
    done
    return 1
}


while read -r file ; do
    files=("${files[@]}" "$file")
done < <(find "${dir[@]}" -type f | sort)


for file1 in "${files[@]}" ; do
    for file2 in "${files[@]}" ; do
            if
                    # check that the file did not match already
                    ! in_list "$file1" "${matched[@]}" &&

                    # check that the files are not the same file
                    ! [ $(stat -f %i "${file1}") -eq $(stat -f %i "${file2}") ] &&

                    # check that the size of the files are the same
                    [ $(stat -f %z "${file1}") = $(stat -f %z "${file2}") ] &&

                    # check that the non-directory part (aka file name) of the two
                    # files match case insensitively
                    grep -q $(lc "${file1##*/}") <<<$(lc "${file2##*/}")
            then
                    matched=("${matched[@]}" "$file1")
                    echo "$file1"
                    break
            fi
    done
done

РЕДАКТИРОВАТЬ: Добавил комментарии и, вдохновленный комментариями TLP, сделал только часть файла пути для сравнения равенства. Теперь это было проверено в разумной минимальной степени, и я ожидаю, что это не взорвется на вашем лице.

1 голос
/ 20 июня 2011

Я не пользовался им лично, но Duplicate Files Finder выглядит так, как будто он подходит.

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

Это открытый исходный код, доступный в Windows и Linux, с интерфейсами командной строки и GUI, и из описания алгоритм звучит очень быстро (сравнивает только файлы одинакового размера, а не выдает контрольную сумму для каждого файла).

0 голосов
/ 25 мая 2017

Сценарий оболочки для перечисления всех имен файлов в рабочем каталоге Subversion, которые отличаются только в случае, если другое имя файла в том же каталоге, и, следовательно, вызовет проблемы для клиентов Subversion в файловых системах без учета регистра, которые не могут различать такие имена файлов:

find . -name .svn -type d -prune -false -o -print | \
perl -ne 'push @{$f{lc($_)}}, $_; END{map{print @{$f{$_}}} grep {@{$f{$_}}>1} sort keys %f}'
0 голосов
/ 23 июля 2013

Вот скрипт Ruby для рекурсивного поиска файлов, которые отличаются только регистром.

#!/usr/bin/ruby
# encoding: utf-8

def search( directory )

    set = {}
    Dir.entries( directory ).each do |entry|
        next if entry == '.' || entry == '..'
        path = File.join( directory, entry )

        key = path.upcase
        set[ key ] = [] unless set.has_key?( key )
        set[ key ] << entry

        search( path ) if File.directory?( path )
    end

    set.delete_if { |key, entries| entries.size == 1 }
    set.each do |key, entries|
        entries.each do |entry|
            puts File.join( directory, entry )
        end
    end

end

search( File.expand_path( ARGV[ 0 ] ) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...