Existe-t-il un moyen de supprimer les doublons plus raffiné que fdupes -rdN?

22

Récemment, j'ai dû supprimer un grand nombre de doublons. Je fusionne trois ou quatre systèmes de fichiers et je souhaite que l'espace soit utilisé de manière économique. Au début, cela fdupessemblait être le meilleur outil pour le travail, mais je rencontre de plus en plus de limites.

Considérez la commande fdupes -rdN somedirectory/. Cela crée un hachage de tous les fichiers dans les sous-répertoires de certains répertoires.

Et quand il rencontre des doublons, il les supprime, de sorte qu'il n'y a qu'une seule copie de tout.

Mais que se passe-t-il si je veux conserver somedirectory/subdirectory1/somefileet qu'il y a, en fait, quatre doublons, et que le programme rencontre d'abord l'un des doublons? Ensuite, il supprime somedirectory/subdirectory1/somefilece que je ne veux pas.

Je veux pouvoir spécifier, en quelque sorte, les doublons à conserver. Et jusqu'à présent, aucun des programmes standard pour gérer les doublons (duff, FSLint) ne semble permettre l'automatisation de ce type de comportement. Je préfère ne pas lancer la mienne, c'est pourquoi je pose cette question.

J'aimerais pouvoir écrire quelque chose comme

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
ixtmixilix
la source
Je cherchais la même chose et j'ai trouvé ce superuser.com/a/561207/218922
alexis

Réponses:

5

Bien que la fonctionnalité que vous recherchez n'est pas disponible en stock fdupes, j'ai bifurqué fdupes (ma fourchette s'appelle jdupes) et j'ai ajouté quelques fonctionnalités qui peuvent résoudre ce problème dans certaines circonstances. Par exemple, dans le cas indiqué où vous souhaitez conserver somedirectory/subdirectory1/somefilelors de la suppression automatique des doublons (les commutateurs det Nensemble) et où il n'y a pas de fichiers séparés immédiatement en dessous somedirectory, vous jdupespouvez alimenter chaque chemin de sous-répertoire immédiat avec subdirectory1first et le -Ocommutateur (qui trie les fichiers par commande -Ordre des paramètres de ligne en premier):

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

Cela supprimera automatiquement tous les fichiers sauf un dans un jeu en double et garantira que si le jeu contient un fichier, somedirectory/subdirectory1il sera le premier, devenant ainsi automatiquement le fichier préservé dans le jeu. Il y a encore des limites flagrantes à cette approche, comme le fait qu'un autre doublon somedirectory/subdirectory1puisse être conservé au lieu de celui que vous vouliez conserver, mais dans un bon nombre de cas comme le vôtre, l' jdupesoption d'ordre des paramètres comme solution de contournement est assez bonne.

Dans un avenir proche, je prévois d'ajouter un système de filtrage jdupesqui permettra un énorme contrôle de l'inclusion / exclusion des fichiers, de la conservation des -Nactions et de l'application de ces "piles de filtres" sur une base globale ou par paramètre. Cette fonctionnalité est absolument nécessaire; J'envisage quelque chose comme ceci pour "supprimer automatiquement les doublons non nuls de manière récursive MAIS toujours conserver somedirectory/subdirectory1/somefiletels quels ":

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

Jody Lee Bruchon
la source
4

Qu'en est-il de l'association des fichiers en double? De cette façon, l'espace n'est utilisé qu'une seule fois, mais ils existent toujours dans tous les chemins. Le hic avec ceci est que les fichiers liés en dur doivent être modifiés sur place (ils ne doivent être modifiés qu'en supprimant le fichier et en le recréant avec le nouveau contenu). L'autre approche consiste à créer un lien symbolique entre les fichiers, bien que vous ayez le même problème de décider quel est le fichier "principal". Cela pourrait être fait avec le script suivant (bien que notez que cela ne gère pas les noms de fichiers contenant des espaces).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
mgorven
la source
1
Utiliser à la jdupesplace de fdupesvous peut simplement aller, jdupes -nrL somedirectory/ce qui est massivement plus rapide.
Jody Lee Bruchon
1
Tapez le lien vers jdupes. Lien pratique: github.com/jbruchon/jdupes
Royce Williams
4

Je n'ai vu celui-ci nulle part ailleurs: dites ce que vous voulez, c'est ceci. Vous avez / mnt / folder-tree-1 / mnt / folder-tree-2. Vous ne voulez pas supprimer chaque dupe, mais si un fichier existe dans l'arborescence-2 et qu'un fichier identique existe dans l'arborescence-1 avec le même chemin d'accès et le même nom, supprimez-le de l'arborescence-2.

Attention: c'est assez laconique et si vous essayez de copier-coller avec des compétences limitées en shell, soyez prudent.

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 tout sur une seule ligne:

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

Ensuite, inspectez et exécutez rm-v2-dupes.sh

Gaute Lund
la source
4

J'avais la même question. Si vous avez plusieurs doublons fdupes /my/directory/ -rdNconserve le fichier avec la date de modification la plus ancienne, ou si plusieurs fichiers ont la même date de modification, alors celui trouvé en premier.

Si la date de modification n'est pas importante pour vous, vous pouvez touchles fichiers dans le répertoire que vous souhaitez conserver. Si vous les choisissez touchavec la date et l'heure actuelles, vous fdupes -rdNiconserverez ceux avec la date actuelle. Vous pouvez également touchconserver les fichiers dont la date est antérieure à celle que vous souhaitez supprimer et utiliser fdupes -rdNnormalement.

Si vous devez conserver la date de modification, vous devrez utiliser l'une des autres méthodes.

pheon
la source
3

Juste pour ajouter une touche à une réponse précédente. J'ai utilisé le code suivant plusieurs fois, modifiant légèrement une réponse précédente avec un simple | greppour isoler le dossier dont je veux supprimer.

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

Encore une fois, cela créera un fichier sh pour supprimer tous les fichiers répertoriés, aucune ligne commentée. Bien sûr, vous pouvez toujours modifier le fichier pour commenter les lignes / fichiers spécifiques que vous souhaitez conserver.

Une autre astuce pour les grands répertoires est d'exécuter fdupes dans un fichier txt, puis d'expérimenter avec | grepet | sedjusqu'à ce que j'obtienne le résultat souhaité.

`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
la source
2

Utilisez sedpour créer un fichier shell qui contiendra des commandes commentées pour supprimer chacun de vos fichiers en double:

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

Le remove-duplicate-files.shfichier résultant que nous venons de créer mettra en commentaire chaque ligne. Décommentez les fichiers que vous souhaitez supprimer. Ensuite, courez sh remove-duplicate-files.sh. Voila!

MISE À JOUR

Eh bien, si vous ne souhaitez pas supprimer des fichiers uniquement dans certains répertoires, c'est aussi simple que cela :

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

exclude_duplicates.pyest:

#/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)

Le remove-duplicate-files-keep-protected.shfichier résultant que nous venons de créer contiendra tous les fichiers des répertoires protégés. Ouvrez ce fichier dans votre éditeur de texte préféré, vérifiez que tout est OK. Ensuite, lancez-le. Voila (sic)!

Ivan Kharlamov
la source
j'y ai pensé, mais ce n'est pas assez automatisé. bêtement, j'ai causé une perte de données avec cette méthode quand il s'agissait de doublons espacés sur plusieurs systèmes de fichiers ... il n'y a aucun moyen d'attribuer une priorité, étant donné la sortie de fdupes. En gros, j'aurais dû parcourir 10 000 fichiers à la main pour éviter cette perte de données ... donc, non merci ... en fait, c'est la perte de données qui est la raison même pour laquelle j'ai posé cette question.
ixtmixilix
@ixtmixilix, eh bien, la méthode manuelle dépend de l'attention des utilisateurs, voici rien de nouveau. Si vous voulez quelque chose de plus automatisé, consultez une réponse mise à jour ci-dessus.
Ivan Kharlamov
2

Et quelque chose comme ça?

#!/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
la source