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

22

Recentemente, tenho a necessidade de excluir muitas duplicatas. Estou mesclando três ou quatro sistemas de arquivos e quero que o espaço seja usado economicamente. No começo, fdupesparecia ser a melhor ferramenta para o trabalho, mas estou cada vez mais enfrentando limitações.

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

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

Mas e se eu quiser ficar com somedirectory/subdirectory1/somefile, na verdade, quatro duplicatas, e o programa encontrar uma das duplicatas primeiro? Então ele apaga somedirectory/subdirectory1/somefile, o que eu não quero.

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

Eu gostaria de poder escrever algo como

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
ixtmixilix
fonte
Eu estava procurando a mesma coisa e encontrei este superusuário.com
561207

Respostas:

5

Embora a funcionalidade que você procura não esteja disponível em estoque fdupes, eu peguei o garfo fdupes (meu garfo é chamado jdupes) e adicionei alguns recursos que podem resolver esse problema em determinadas circunstâncias. Por exemplo, no caso declarado em que você deseja manter somedirectory/subdirectory1/somefilea exclusão automática de duplicatas ( de Nalterna juntas) e não há arquivos separados imediatamente abaixo somedirectory, jdupespode ser alimentado cada caminho de subdiretório imediato com subdirectory1first e a -Oopção (que classifica os arquivos por comando primeiro parâmetro da linha):

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, somedirectory/subdirectory1ele será o primeiro, tornando-se automaticamente o arquivo preservado no conjunto. Ainda existem limites evidentes para essa abordagem, como o fato de que outra duplicata somedirectory/subdirectory1pode ser preservada em vez da que você deseja manter, mas em um bom número de casos como o seu, a jdupesopção de ordem de parâmetro como solução alternativa é boa o suficiente.

Em um futuro próximo, pretendo adicionar um sistema de filtragem jdupesque permita uma enorme quantidade de controle sobre a inclusão / exclusão de arquivos, preservação de -Nações e aplicação dessas "pilhas de filtros" em uma base global ou por parâmetro. Esse recurso é extremamente necessário; Eu imagino algo assim para "excluir automaticamente duplicatas diferentes de zero recursivamente, mas sempre preservar somedirectory/subdirectory1/somefilecomo está":

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

Jody Lee Bruchon
fonte
4

E quanto a vincular os arquivos duplicados juntos? Dessa forma, o espaço é usado apenas uma vez, mas eles ainda existem em todos os caminhos. O problema é que os arquivos com links físicos devem ser modificados no local (eles devem ser modificados apenas para excluir o arquivo e recriá-lo com o novo conteúdo). A outra abordagem é vincular os arquivos juntos, embora você tenha o mesmo problema de decidir qual é o arquivo "primário". Isso pode ser feito com o seguinte script (embora observe que isso não trata nomes de arquivos que contêm espaços).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
mgorven
fonte
1
Usando em jdupesvez de fdupesvocê pode simplesmente ir, o jdupes -nrL somedirectory/que é massivamente mais rápido.
Jody Lee Bruchon
1
Erro de digitação no link para jdupes. Link de conveniência: github.com/jbruchon/jdupes
Royce Williams
4

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

Aviso: isso é bem conciso e se você tentar copiar e colar isso com habilidades limitadas de shell, 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

Gaute Lund
fonte
4

Eu tive a mesma pergunta. Se você tiver muitas duplicatas, fdupes /my/directory/ -rdNmanterá o arquivo com a data de modificação mais antiga ou se vários arquivos tiverem a mesma data de modificação, o que foi encontrado primeiro.

Se a data de modificação não for importante para você, você pode touchos arquivos no diretório que deseja manter. Se você optar por toucheles com a data e hora atuais fdupes -rdNi, manterá os com a data atual. Ou você pode touchmanter os arquivos com uma data anterior à daqueles que você deseja excluir e usar fdupes -rdNnormalmente.

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

pheon
fonte
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 | greppara isolar a pasta da qual quero 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 deseja manter.

Outra dica para diretórios grandes é executar fdupes em um arquivo txt, depois experimentar | grepe | sedaté 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`
jfl
fonte
2

Use sedpara criar um arquivo shell que conterá comandos comentados para excluir cada um de seus arquivos duplicados:

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

O remove-duplicate-files.sharquivo resultante que acabamos de criar terá cada linha comentada. Descomente os arquivos que você deseja excluir. Então corra sh remove-duplicate-files.sh. Voila!

ATUALIZAR

Bem, se você não deseja excluir arquivos apenas em determinados diretórios, é simples assim :

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.pyfica:

#/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 remove-duplicate-files-keep-protected.sharquivo resultante que acabamos de criar terá todos os arquivos dos diretórios protegidos comentados. Abra este arquivo no seu editor de texto favorito, verifique se está tudo bem. Então execute. Voila (sic)!

Ivan Kharlamov
fonte
Eu pensei nisso, mas não é automatizado o suficiente. estupidamente, eu causei perda de dados com esse método ao lidar com duplicatas espaçadas em vários sistemas de arquivos ... não há como atribuir uma prioridade, dada a saída de fdupes. Basicamente, eu teria que vasculhar 10000 arquivos manualmente, a fim de impedir a perda de dados ... então, não, obrigado ... de fato, essa perda de dados é o motivo pelo qual eu fiz essa pergunta.
precisa saber é o seguinte
@ixtmixilix, bem, o método manual depende da atenção do usuário, aqui não há nada de novo. Se você quiser algo mais automatizado, confira uma resposta atualizada acima.
Ivan Kharlamov
2

Que tal 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
Rynchodon
fonte