
Recentemente, tive a necessidade de excluir muitas duplicatas. Estou mesclando três ou quatro sistemas de arquivos e quero que o espaço seja usado economicamente. A princípio fdupes
parecia a melhor ferramenta para o trabalho, mas cada vez mais encontro limitações.
Considere o comando fdupes -rdN somedirectory/
. Isso cria um hash de todos os arquivos nos subdiretórios de algum diretório.
E quando encontra duplicatas, ele as apaga, 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 uma das duplicatas primeiro? Então ele exclui 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/
Responder1
Embora a funcionalidade que você procura não esteja disponível em estoque fdupes
, eu bifurqueifdupes
(meu garfo se chama jdupes
)e adicionou alguns recursos que podem resolver esse problema em determinadas circunstâncias. Por exemplo, no caso declarado em que você deseja manter somedirectory/subdirectory1/somefile
ao excluir automaticamente duplicatas (as opções d
e N
juntas) e não há arquivos separados imediatamente abaixo de somedirectory
, jdupes
pode ser alimentado cada caminho de subdiretório imediato com subdirectory1
first e a -O
opção (que classifica os arquivos por comando -line ordem dos parâmetros primeiro):
jdupes -rdNO 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/subdirectory1
ele 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/subdirectory1
pode ser preservada em vez daquela que você deseja manter, mas em um bom número de casos como o seu, a jdupes
opção de ordem de parâmetro como solução alternativa é boa o suficiente.
Num futuro próximo, pretendo adicionar um sistema de filtragem jdupes
que permitirá um enorme controle sobre a inclusão/exclusão de arquivos, preservação para -N
ações e aplicação de tais "pilhas de filtros" de forma global ou por parâmetro. Este recurso é extremamente necessário; Eu imagino algo assim para "excluir automaticamente duplicatas diferentes de zero recursivamente, MAS sempre preservar somedirectory/subdirectory1/somefile
como estão":
jdupes -rdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/
ATUALIZAÇÃO (01/03/2022):Dê uma olhada nas -X
opções de filtro estendido adicionadas em 2020. Não é exatamente o que você deseja, mas os filtros nostr
e onlystr
permitem especificar substrings dentro de um caminho completo para ignorar ou exigir.
Responder2
Não vi este 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 idiotas, mas se existir um arquivo na árvore-2 e um arquivo idêntico existir na árvore-1 com exatamente o mesmo caminho e nome, remova-o da árvore-2.
Aviso: isso é bastante conciso e se você tentar copiar e colar com habilidades limitadas de shell, tome 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
Responder3
Que tal vincular os arquivos duplicados? Dessa forma o espaço é utilizado apenas uma vez, mas ainda existem em todos os caminhos. O problema é que os arquivos com link físico devem ser modificados no local (eles só devem ser modificados excluindo o arquivo e recriando-o com o novo conteúdo). A outra abordagem é vincular os arquivos simbolicamente, embora você tenha o mesmo problema ao decidir qual é o arquivo "primário". Isso poderia ser feito com o script a seguir (embora observe que isso não lida com 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
Responder4
Apenas para adicionar um toque especial a uma resposta anterior. Usei o código a seguir várias vezes, modificando ligeiramente uma resposta anterior com um simples | grep
isolamento da pasta da qual desejo 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 e experimentar | 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`