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 de maneira econômica. No início, fdupes
parecia ser a melhor ferramenta para o trabalho, mas estou cada vez mais encontrando 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, as apaga, de modo que há apenas uma cópia de tudo.
Mas e se eu quiser manter somedirectory/subdirectory1/somefile
e existem, de fato, quatro duplicatas, e o programa encontra uma das duplicatas primeiro? Em seguida, ele exclui somedirectory/subdirectory1/somefile
, que eu não quero.
Quero ser capaz de especificar, de alguma forma, quais duplicatas manter. E até agora, nenhuma dos programas padrão para lidar com duplicatas (duff, FSLint) parecem 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
Comentários
- Eu estava procurando a mesma coisa e encontrei esta superuser.com/a/561207/218922
Resposta
Embora a funcionalidade que você busca não esteja disponível em estoque fdupes
, eu bifurquei fdupes
(meu fork se chama jdupes
) e adicionou alguns recursos que podem resolver esse problema em certas circunstâncias. exemplo, no caso indicado em que deseja manter somedirectory/subdirectory1/somefile
ao excluir duplicatas automaticamente (as chaves d
e N
juntas) e não há arquivos separados imediatamente abaixo de somedirectory
, jdupes
pode ser alimentado em cada caminho de subdiretório imediato com subdirectory1
primeiro e o -O
switch (que classifica os arquivos pela ordem dos parâmetros da linha de comando primeiro):
jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3
Isso irá 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 há limites flagrantes para essa abordagem, como o fato de que outra duplicata em somedirectory/subdirectory1
pode ser preservada em vez da que você deseja manter, mas em um bom número de casos como o seu, a opção de ordem do parâmetro jdupes
como uma solução alternativa é boa o suficiente.
Em um futuro próximo, pretendo adicionar um sistema de filtragem a jdupes
que permitirá uma grande quantidade de controle sobre a inclusão / exclusão de arquivos, preservação para -N
ações e aplicação de tais “pilhas de filtros” em um global ou por -parâmetro base. Esse recurso é extremamente necessário; Eu imagino algo assim para “excluir automaticamente duplicatas diferentes de zero recursivamente, MAS sempre preservar somedirectory/subdirectory1/somefile
como está”:
jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/
Resposta
Eu não vi este em nenhum outro lugar: diga o que você quer é isso. / mnt / folder-tree-1 / mnt / folder-tree-2. Você não deseja remover todos os duplicados, mas se existir um arquivo em tree-2 e um arquivo idêntico existir em tree-1 exatamente com o mesmo caminho e nome, remova-o da árvore-2.
Aviso: é muito conciso e se você tentar copiar e colar com habilidades de shell 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
Resposta
Que tal vincular os arquivos duplicados juntos? Dessa forma o espaço é usado apenas uma vez, mas eles ainda existem em todos os caminhos. O problema com isso é 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 simbolicamente os arquivos, embora você tenha o mesmo problema de decidir qual é o arquivo “principal”. Isso poderia ser feito com o seguinte script (embora observe que ele não lida com nomes de arquivo que contenham espaços).
fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do for DEST in $DESTS; do ln -f $SOURCE $DEST done done
Comentários
Resposta
Eu tinha a mesma pergunta.Se você tiver muitas duplicatas, fdupes /my/directory/ -rdN
mantém o arquivo com a data de modificação mais antiga, ou se vários arquivos têm a mesma data de modificação, então aquele encontrado primeiro.
Se a data de modificação não for importante para você, você pode touch
os arquivos no diretório que deseja manter. Se você escolher touch
com a data e hora atuais, fdupes -rdNi
manterá aqueles com a data atual. Ou você pode touch
manter os arquivos com uma data anterior àquela que deseja excluir e usar fdupes -rdN
normalmente.
Se precisar manter a data de modificação, você precisará usar um dos outros métodos.
Resposta
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 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, em seguida, experimentar | grep
e | sed
até eu obter o resultado que eu quero.
`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`
Resposta
Use sed
para 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.sh
fil O que acabamos de criar terá cada linha comentada. Remova o comentário dos arquivos que deseja excluir. Em seguida, execute sh remove-duplicate-files.sh
. Voila!
ATUALIZAÇÃO
Bem, se você não quiser excluir arquivos apenas em determinados diretórios, é tão simples quanto este :
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 resultado remove-duplicate-files-keep-protected.sh
arquivo que acabamos de criar terá todos os arquivos de diretórios protegidos comentados. Abra este arquivo em seu editor de texto favorito, verifique se está tudo OK. Em seguida, execute-o. Voila (sic)!
Comentários
- eu pensei nisso, mas ‘ não é automatizado o suficiente. Estupidamente, eu causei perda de dados com este método quando lidar com duplicatas espaçadas em vários sistemas de arquivos … não há ‘ nenhuma maneira de atribuir uma prioridade, dada a saída de fdup es. basicamente, eu teria que vasculhar 10.000 arquivos manualmente para evitar a perda de dados … então, não, obrigado … na verdade, essa perda de dados é a razão pela qual fiz esta pergunta.
- @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, verifique uma resposta atualizada acima.
Resposta
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
Resposta
A partir do Apple Filesystem (APFS), outra solução é manter os arquivos, desduplicá-los e não ter impacto no uso do disco. Veja Substituir arquivos duplicados existentes no APFS por clones
jdupes
em vez defdupes
você pode simplesmente irjdupes -nrL somedirectory/
que é muito mais rápido.