Existe uma maneira de excluir duplicatas mais refinado que fdupes -rdN?

19

Recentemente, tenho a necessidade de excluir muitos duplicados. Estou fundindo três ou quatro sistemas de arquivos e quero que o espaço seja usado economicamente. No começo, fdupes parecia ser a melhor ferramenta para o trabalho, mas estou cada vez mais correndo por limitações.

Considere o comando fdupes -rdN somedirectory/ . Isso faz um hash de todos os arquivos nos subdiretórios de algum diretório.

E quando encontra duplicatas, elimina-as, para que haja apenas uma cópia de tudo.

Mas e se eu quiser manter somedirectory/subdirectory1/somefile e houver, de fato, quatro duplicatas e o programa encontrar um dos duplicados primeiro? Em seguida, elimina somedirectory/subdirectory1/somefile , o que não quero.

Eu quero ser capaz de especificar, de alguma forma, quais duplicatas devem ser mantidas. E até agora, nenhum dos programas padrão para lidar com duplicatas (duff, FSLint) parece permitir a automação desse tipo de comportamento. Eu prefiro não fazer o meu próprio, então é por isso que estou fazendo esta pergunta.

Eu gostaria de poder escrever algo como

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
    
por ixtmixilix 16.03.2012 / 22:56

7 respostas

4

E quanto a vincular os arquivos duplicados? Dessa forma, o espaço é usado apenas uma vez, mas eles ainda existem em todos os caminhos. O problema é que os arquivos com hardlink devem ser modificados (eles devem ser modificados apenas para excluir o arquivo e recriá-lo com o novo conteúdo). A outra abordagem é ligar simbolicamente os arquivos juntos, embora você tenha o mesmo problema de decidir qual arquivo "principal" é. Isso pode ser feito com o seguinte script (embora note que isso não manipula nomes de arquivos contendo espaços).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
    
por 26.03.2012 / 03:40
4

Eu não vi esse em outro lugar: diga o que você quer é isso. Você tem / mnt / folder-tree-1 / mnt / folder-tree-2. Você não quer remover todos os dupes, mas se um arquivo existir em tree-2, e um arquivo idêntico existir em tree-1 com exatamente o mesmo caminho e nome, remova-o da tree-2.

Atenção: isso é bastante conciso e se você tentar copiá-lo e colá-lo com habilidades limitadas, tenha cuidado.

fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt

fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line
do
if grep -q "'echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|''" dupes-all.txt
then
    echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2//|')\"
fi
done > rm-v2-dupes.sh

Ou tudo em uma linha:

fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt; fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line; do if grep -q "'echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|''" dupes-all.txt; then echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|')\"; fi; done > rm-v2-dupes.sh

Depois, inspecione e execute rm-v2-dupes.sh

    
por 24.09.2012 / 12:43
3

Apenas para adicionar um toque a uma resposta anterior. Eu usei o código a seguir várias vezes, modificando ligeiramente uma resposta anterior com um simples | grep para isolar a pasta da qual deseja excluir.

'fdupes -r -n -S /directory | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh'

Novamente, isso criará um arquivo sh para excluir todos os arquivos listados, sem linhas comentadas. É claro que você ainda pode editar o arquivo para comentar linhas / arquivos específicos que você deseja manter.

Outra dica para diretórios grandes é executar o fdupes em um arquivo txt e, em seguida, experimentar com | grep e | sed até obter o resultado desejado.

'fdupes -r -n -S /directory > duplicate-files.txt'
'cat duplicate-files.txt | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh'
    
por 20.04.2017 / 06:08
2

Use sed para criar um arquivo de shell que conterá comentários comentados comandos para excluir cada um dos seus arquivos duplicados:

fdupes -r -n -S /directory | sed -r "s/^/#rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh

O arquivo resultante remove-duplicate-files.sh que acabamos de criar terá cada linha comentada. Descomente os arquivos que você deseja excluir. Em seguida, execute sh remove-duplicate-files.sh . Voila!

UPDATE

Bem, se você não quiser excluir arquivos apenas em determinados diretórios, é tão simples quanto isso :

fdupes -S /directory|sed '/^$/d' |sed -r "s/^[0-9]/#&/" > duple_list

python exclude_duplicates.py -f /path/to/dupe_list --delimiter='#' --keep=/full/path/to/protected/directory1,/full/path/to/protected/directory2\ with\ spaces\ in\ path >remove-duplicate-files-keep-protected.sh

Onde exclude_duplicates.py é:

#/usr/bin/python
# -*- coding: utf-8 -*-
# exclude_duplicates.py
"""
THE SCRIPT DOESN'T DELETE ANYTHING, IT ONLY GENERATES TEXT OUTPUT.
Provided a list of duplicates, such as fdupes or fslint output,
generate a bash script that will have all duplicates in protected
directories commented out. If none of the protected duplicates are
found in a set of the same files, select a random unprotected
duplicate for preserving.
Each path to a file will be transformed to an 'rm "path"' string which
will be printed to standard output.     
"""

from optparse import OptionParser
parser = OptionParser()
parser.add_option("-k", "--keep", dest="keep",
    help="""List of directories which you want to keep, separated by commas. \
        EXAMPLE: exclude_duplicates.py --keep /path/to/directory1,/path/to/directory\ with\ space\ in\ path2""",
    metavar="keep"
)
parser.add_option("-d", "--delimiter", dest="delimiter",
    help="Delimiter of duplicate file groups", metavar="delimiter"
)
parser.add_option("-f", "--file", dest="file",
    help="List of duplicate file groups, separated by delimiter, for example, fdupes or fslint output.", metavar="file"
)

(options, args) = parser.parse_args()
directories_to_keep = options.keep.split(',')
file = options.file
delimiter = options.delimiter

pretty_line = '\n#' + '-' * 35
print '#/bin/bash'
print '#I will protect files in these directories:\n'
for d in directories_to_keep:
    print '# ' + d
print pretty_line

protected_set = set()
group_set = set()

def clean_set(group_set, protected_set, delimiter_line):
    not_protected_set = group_set - protected_set
    while not_protected_set:
        if len(not_protected_set) == 1 and len(protected_set) == 0:
            print '#randomly selected duplicate to keep:\n#rm "%s"' % not_protected_set.pop().strip('\n')
        else:
            print 'rm "%s"' % not_protected_set.pop().strip('\n')
    for i in protected_set: print '#excluded file in protected directory:\n#rm "%s"' % i.strip('\n')
    print '\n#%s' % delimiter_line
file = open(file, 'r')
for line in file.readlines():
    if line.startswith(delimiter):
        clean_set(group_set, protected_set, line)
        group_set, protected_set = set(), set()
    else:
        group_set = group_set|{line}
        for d in directories_to_keep:
            if line.startswith(d): protected_set = protected_set|{line}
else:
    if line: clean_set(group_set, protected_set, line)

O arquivo remove-duplicate-files-keep-protected.sh resultante que acabamos de criar terá todos os arquivos de diretórios protegidos comentados. Abra este arquivo no seu editor de texto favorito, verifique se tudo está OK. Então corra. Voila (sic)!

    
por 18.03.2012 / 15:56
2

E algo assim?

#!/bin/bash

DUPE_SEARCH_DIR=somedir/
PREFERRED_DIRS=("somedir/subdir1" "somedir/subdir2")
DUPE_FILE=/tmp/'basename $0'_found-duplicates

delete_dupes() {
    while read line ; do
        if [ -n "$line" ] ; then
            matched=false
            for pdir in "${PREFERRED_DIRS[@]}" ; do
                if [[ $line == $pdir/* ]] ; then
                    matched=true
                    break
                fi
            done
            if ! $matched ; then
                rm -v "$line"
            fi
        fi
    done < "$DUPE_FILE"
}

cleanup() {
    rm -f $DUPE_FILE
}

trap cleanup EXIT

# get rid of normal dupes, preserve first & preserve preferred
fdupes -rf "$DUPE_SEARCH_DIR" > $DUPE_FILE
delete_dupes

# get rid of preserve dupes, preserve preferred
fdupes -r "$DUPE_SEARCH_DIR" > "$DUPE_FILE"
delete_dupes
    
por 16.04.2012 / 01:25
2

Embora a funcionalidade que você procura não esteja disponível no estoque fdupes , eu bifurquei fdupes (meu fork é chamado jdupes ) e adicionou alguns recursos que podem resolver este problema sob certas circunstâncias. Por exemplo, no caso declarado em que você deseja manter somedirectory/subdirectory1/somefile ao excluir automaticamente duplicatas (o d e N alterna) e não há arquivos separados imediatamente abaixo de somedirectory , jdupes pode ser fornecido cada caminho de subdiretório imediato com subdirectory1 primeiro e a opção -O (que classifica os arquivos pela ordem dos parâmetros da linha de comando primeiro):

jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3

Isso excluirá automaticamente todos os arquivos, exceto um, em um conjunto duplicado e garantirá que, se o conjunto contiver um arquivo em somedirectory/subdirectory1 , ele será o primeiro, tornando-se automaticamente o arquivo preservado no conjunto. Ainda existem limites gritantes para essa abordagem, como o fato de que outra duplicata em somedirectory/subdirectory1 pode ser preservada em vez da que você queria manter, mas em um bom número de casos como o seu, a opção de ordem de parâmetro jdupes como solução alternativa é boa o suficiente.

Num futuro próximo, planejo adicionar um sistema de filtragem a jdupes que permitirá um enorme controle sobre a inclusão / exclusão de arquivos, a preservação de -N actions e a aplicação dessas "pilhas de filtros" em uma base global ou por parâmetro. Esse recurso é extremamente necessário; Eu imagino algo como isso para "auto-excluir duplicatas não-zero recursivamente, mas sempre preservar somedirectory/subdirectory1/somefile as-is":

jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/

    
por 07.03.2016 / 07:00
2

Eu tive a mesma pergunta. Se você tiver muitas duplicatas fdupes -rdN mantém o arquivo com a data de modificação mais antiga ou se vários arquivos tiverem a mesma data de modificação, o primeiro encontrado.

Se a data de modificação não for importante para você, você poderá touch dos arquivos no diretório que deseja manter. Se você escolher touch com a data e hora atuais, fdupes -rdNi manterá os com a data atual. Ou você pode touch manter os arquivos com uma data anterior àquela que você deseja excluir e usar fdupes -rdN como normal.

Se você precisar manter a data de modificação, precisará usar um dos outros métodos.

    
por 12.12.2016 / 18:42