Trouver des fichiers en double et les remplacer par des liens symboliques

16

J'essaie de trouver un moyen de vérifier dans un répertoire donné des fichiers en double (même avec des noms différents) et de les remplacer par des liens symboliques pointant vers la première occurrence. J'ai essayé avec fdupesmais il répertorie simplement ces doublons.
C'est le contexte: je personnalise un thème d'icônes à mon goût, et j'ai constaté que de nombreuses icônes, même si elles ont des noms différents et des emplacements différents dans leur dossier parent, et sont utilisées à des fins différentes, sont fondamentalement les mêmes image. Puisqu'appliquer la même modification vingt ou trente fois est redondant quand une seule est vraiment nécessaire, je veux garder une seule image et créer un lien symbolique entre toutes les autres.

Par exemple, si je cours fdupes -r ./dans le répertoire testdir, cela pourrait me renvoyer les résultats suivants:

./file1.png
./file2.png
./subdir1/anotherfile.png
./subdir1/subdir2/yetanotherfile.png

Compte tenu de cette sortie, je voudrais conserver uniquement le fichier file1.png, supprimer tous les autres et les remplacer par des liens symboliques pointant vers lui, tout en conservant tous les noms de fichiers d'origine. Ainsi file2.pngconservera son nom, mais deviendra un lien vers file1.pngau lieu d'être un doublon.

Ces liens ne doivent pas pointer vers un chemin absolu, mais doivent être relatifs au parent testdir répertoire ; c'est-à yetanotherfile.png- dire pointera vers ../../file1.png, pas vers/home/testuser/.icons/testdir/file1.png

Je m'intéresse à la fois aux solutions qui impliquent une interface graphique et une CLI. Il n'est pas obligatoire de l'utiliser, fdupesje l'ai cité car c'est un outil que je connais, mais je suis ouvert à des solutions qui utilisent également d'autres outils.

Je suis à peu près sûr qu'un script bash pour gérer tout cela ne devrait pas être si difficile à créer, mais je ne suis pas assez expert pour savoir comment l'écrire moi-même.

Sekhemty
la source

Réponses:

3

Premier; Y a-t-il une raison pour laquelle vous devez utiliser des liens symboliques et non les liens physiques habituels? J'ai du mal à comprendre le besoin de liens symboliques avec des chemins relatifs. Voici comment je résoudrais ce problème:

Je pense que la version Debian (Ubuntu) de fdupes peut remplacer les doublons par des liens durs en utilisant l' -Loption, mais je n'ai pas d'installation Debian pour le vérifier.

Si vous n'avez pas de version avec l' -Loption, vous pouvez utiliser ce petit script bash que j'ai trouvé sur commandlinefu .
Notez que cette syntaxe ne fonctionnera qu'en bash.

fdupes -r -1 path | while read line; do master=""; for file in ${line[*]}; do if [ "x${master}" == "x" ]; then master=$file; else ln -f "${master}" "${file}"; fi; done; done

La commande ci-dessus trouvera tous les fichiers en double dans "chemin" et les remplacera par des liens physiques. Vous pouvez le vérifier en exécutant ls -ilRet en regardant le numéro d'inode. Voici un samle avec dix fichiers identiques:

$ ls -ilR

total 20
3094308 -rw------- 1 username group  5 Sep 14 17:21 file
3094311 -rw------- 1 username group  5 Sep 14 17:21 file2
3094312 -rw------- 1 username group  5 Sep 14 17:21 file3
3094313 -rw------- 1 username group  5 Sep 14 17:21 file4
3094314 -rw------- 1 username group  5 Sep 14 17:21 file5
3094315 drwx------ 1 username group 48 Sep 14 17:22 subdirectory

./subdirectory:
total 20
3094316 -rw------- 1 username group 5 Sep 14 17:22 file
3094332 -rw------- 1 username group 5 Sep 14 17:22 file2
3094345 -rw------- 1 username group 5 Sep 14 17:22 file3
3094346 -rw------- 1 username group 5 Sep 14 17:22 file4
3094347 -rw------- 1 username group 5 Sep 14 17:22 file5

Tous les fichiers ont des numéros d'inode séparés, ce qui en fait des fichiers séparés. Permet maintenant de les dédupliquer:

$ fdupes -r -1 . | while read line; do j="0"; for file in ${line[*]}; do if [ "$j" == "0" ]; then j="1"; else ln -f ${line// .*/} $file; fi; done; done
$ ls -ilR
.:
total 20
3094308 -rw------- 10 username group  5 Sep 14 17:21 file
3094308 -rw------- 10 username group  5 Sep 14 17:21 file2
3094308 -rw------- 10 username group  5 Sep 14 17:21 file3
3094308 -rw------- 10 username group  5 Sep 14 17:21 file4
3094308 -rw------- 10 username group  5 Sep 14 17:21 file5
3094315 drwx------  1 username group 48 Sep 14 17:24 subdirectory

./subdirectory:
total 20
3094308 -rw------- 10 username group 5 Sep 14 17:21 file
3094308 -rw------- 10 username group 5 Sep 14 17:21 file2
3094308 -rw------- 10 username group 5 Sep 14 17:21 file3
3094308 -rw------- 10 username group 5 Sep 14 17:21 file4
3094308 -rw------- 10 username group 5 Sep 14 17:21 file5

Les fichiers ont désormais tous le même numéro d'inode, ce qui signifie qu'ils pointent tous vers les mêmes données physiques sur le disque.

J'espère que cela résout votre problème ou au moins vous pointe dans la bonne direction!

arnefm
la source
Je me suis rappelé que fdupes avait une option pour remplacer les dupes par des liens, @arnefm mais je ne vois rien dans l' homme ni une option dans v1.51(Ubuntu 14.04.2 LTS).
Alastair
Ma fourchette jdupessur github.com/jbruchon/jdupes a l' -Loption qui fait la liaison dure souhaitée des jeux en double.
Jody Lee Bruchon
Je viens de peaufiner le script ici. Il ne gérera toujours pas les espaces, mais gérera d'autres caractères spéciaux (j'avais des chaînes de requête URL dans les fichiers). De plus, la ${line//…/}partie ne fonctionnait pas pour moi, j'ai donc fait un moyen plus propre d'obtenir le premier fichier "maître" en lien dur.
IBBoard
1
Aurions-nous besoin de liens logiciels relatifs si nous utilisons rsyncun autre type de système de fichiers? Ou si le système de fichiers ne préserve pas la hiérarchie, par exemple, c'est un serveur de sauvegarde qui met tout sous /«machine-name»/...? Ou si vous souhaitez restaurer à partir d'une sauvegarde? Je ne vois pas comment les liens physiques vont être conservés ici. Les liens logiciels relatifs auraient plus de chances de survivre, je pense.
Buddy
6

Si vous n'aimez pas beaucoup les scripts, je peux recommander rdfind . Qui analysera les répertoires donnés pour les fichiers en double et les liera entre eux en dur ou en soft. Je l'ai utilisé pour dédupliquer mon répertoire Ruby Gems avec beaucoup de succès. Il est disponible dans Debian / Ubuntu.

Andrew France
la source
4

J'ai eu une situation similaire, mais dans mon cas, le lien symbolique doit pointer vers un chemin relatif, j'ai donc écrit ce script python pour faire l'affaire:

#!/usr/bin/env python
# Reads fdupes(-r -1) output and create relative symbolic links for each duplicate
# usage: fdupes -r1 . | ./lndupes.py

import os
from os.path import dirname, relpath, basename, join
import sys

lines = sys.stdin.readlines()

for line in lines:
    files = line.strip().split(' ')
    first = files[0]
    print "First: %s "% first
    for dup in files[1:]:
        rel = os.path.relpath(dirname(first), dirname(dup))
        print "Linking duplicate: %s to %s" % (dup, join(rel,basename(first)))
        os.unlink(dup)
        os.symlink(join(rel,basename(first)), dup)

Pour chaque ligne d'entrée (qui est une liste de fichiers), le script fractionne la liste de fichiers (séparés par des espaces), obtient le chemin relatif de chaque fichier au premier, puis crée le lien symbolique.

filipenf
la source
1

Ainsi, la réponse donnée par arnefm (qui a été copiée partout sur Internet) ne traite pas des espaces dans les noms de fichiers. J'ai écrit un script qui traite des espaces dans les fichiers.

#!/bin/bash
fdupes -r -1 CHANGE_THIS_PATH | sed -e 's/\(\w\) /\1|/g' -e 's/|$//' > files
while read line; do
        IFS='|' read -a arr <<< "$line"
        orig=${arr[0]}
        for ((i = 1; i < ${#arr[@]}; i++)); do
                file="${arr[$i]}"
                ln -sf "$orig" "$file"
        done 
done < files

Ce que cela fait, c'est de trouver des dupes et de les écrire PIPE séparés dans un fichier nommé 'files'.

Ensuite, il lit le fichier, ligne par ligne, dans un tableau, et chaque élément du tableau est délimité par le PIPE.

Il itère ensuite sur tous les éléments non premiers du tableau, en remplaçant le fichier par un lien symbolique vers le premier élément.

Le fichier externe («fichiers») pourrait être supprimé, si la commande fdupes est exécutée dans un sous-shell, qui est lu directement par le temps, mais cette façon semble plus claire.

David Ventura
la source
2
Cette version traite-t-elle des fichiers avec des noms contenant un canal? Je suppose qu'aucune des versions ne gère les noms de fichiers contenant des sauts de ligne, mais c'est une limitation des fdupes plutôt que toute autre chose.
dhag
Ce n'est pas le cas, mais vous pouvez définir IFS à tout ce que vous voulez (modifiez également la valeur dans le remplacement sed), alors vous ne devriez pas avoir de problème (IFS à 'ñ' ou quelque chose comme ça devrait fonctionner)
David Ventura
Cela crée des liens symboliques brisés et j'ai des fichiers liés à eux-mêmes. NE PAS UTILISER
MrMesees
0

Quelques mises en garde à l'avant:

  • Spécifique BASH
  • Pas d'espace dans les noms de fichiers
  • Suppose que chaque ligne contient au plus 2 fichiers.

fdupes -1r common/base/dir | while read -r -a line ; do ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}; done

Si plus de 2 fichiers sont en double (par exemple, file1 file2 file3), nous devons créer un lien symbolique pour chaque paire - traiter file1, file2 et file1, file3 comme 2 cas distincts:

if [[ ${#line[@]} -gt 2 ]] ;then 
  ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]} 
  ln -sf $(realpath --relative-to ${line[2]} ${line[0]}) ${line[2]} 
  ...
fi

Le dépenser pour gérer automatiquement un nombre arbitraire de doublons par ligne demandera un peu plus d'efforts.

Une autre approche serait de créer d'abord des liens symboliques vers des chemins absolus, puis de les convertir:

fdupes -1r /absolute/path/common/base/dir | while read -r -a line ; do ln -sf ${line[0]} ${line[1]}; done
chroot /absolute/path/common/base/dir ; symlinks -cr .

Ceci est basé sur la réponse de @Gilles: /unix//a/100955/77319

Dani_l
la source