Briser tous les liens physiques dans un dossier

10

J'ai un dossier qui contient un certain nombre de fichiers qui ont des liens durs (dans le même dossier ou ailleurs), et je veux dissocier ces fichiers, afin qu'ils deviennent indépendants, et les modifications de leur contenu n'affecteront aucun autre fichier (leur nombre de liens devient 1).

Ci-dessous, je donne une solution qui copie essentiellement chaque lien dur vers un autre emplacement, puis le remets en place.

Cependant, cette méthode semble plutôt grossière et sujette aux erreurs, donc j'aimerais savoir s'il existe une commande qui supprimera le lien physique d'un fichier pour moi.

Réponse grossière:

Rechercher des fichiers qui ont des liens durs ( Éditer : pour trouver également des sockets etc. qui ont des liens durs , utilisez find -not -type d -links +1):

find      -type f -links +1 # files only
find -not -type d -links +1 # files, sockets etc.

Une méthode grossière pour supprimer le lien physique d'un fichier (copiez-le vers un autre emplacement et déplacez-le en arrière): Modifier: Comme Celada l'a dit, il est préférable de faire un cp -p ci-dessous, pour éviter de perdre les horodatages et les autorisations. Modifier: créez un répertoire temporaire et copiez-le dans un fichier en dessous, au lieu d'écraser un fichier temporaire, cela minimise le risque d'écraser certaines données, bien que la mvcommande soit toujours risquée (merci @Tobu). Modifier: essayez de créer le répertoire temporaire dans le même système de fichiers (@MikkoRantalainen).

# This is unhardlink.sh
set -e
for i in "$@"; do
  temp="$(mktemp -d -- "${i%/*}/hardlnk-XXXXXXXX")"
  [ -e "$temp" ] && cp -ip "$i" "$temp/tempcopy" && mv "$temp/tempcopy" "$i" && rmdir "$temp"
done

Donc, pour dissocier tous les liens physiques ( Modifier : changé -type fen -not -type d, voir ci-dessus):

find -not -type d -links +1 -print0 | xargs -0 unhardlink.sh
Suzanne Dupéron
la source
Je ne considérerais pas cela comme «brut». La seule façon d'accélérer cela est probablement de faire un tour avec l'appel système sendfile () et de dissocier le fichier open source et de réécrire la cible sur place. Franchement, cela ne vaut pas la peine.
Matthew Ife
Par `` brut '', je veux dire que, par exemple, lorsque j'ai exécuté cette commande à l'aide du cp -icommutateur, il m'a craché quelques messages lui demandant s'il devait remplacer ./fileXXXXXX(le $tempfichier), même si tmpfile devrait donner des noms de fichier uniques, il doit donc y avoir être une sorte de condition de concurrence ou autre, et avec elle le risque de perdre certaines données.
Suzanne Dupéron
1
Il est normal que le fichier existe, vous venez de le créer avec tempfile (nb: obsolète au profit de mktemp, mais ce n'est pas ce qui a causé votre problème).
Tobu
1
Vous unhardlink.shdevez créer un répertoire temporaire dans le même répertoire qui contient le fichier qui doit être non lié. Sinon, votre appel récursif risque de rentrer dans un autre système de fichiers et vous finirez par déplacer des éléments au-delà des limites du système de fichiers car votre répertoire temporaire se trouve dans le répertoire de travail actuel. Je suppose que vous pourriez "$(dirname "$i")/hardlink-XXXXXX"plutôt passer l'argument à mktemp.
Mikko Rantalainen
1
@MikkoRantalainen Merci beaucoup, mis à jour! Notez que si le système de fichiers est une sorte d'unionfs ou un fusesystème de fichiers, il peut en fait être distribué path/to/hardlink-XXXsur un autre support de stockage physique path/to/original-file, mais il n'y a pas grand-chose à faire à ce sujet.
Suzanne Dupéron

Réponses:

9

Il y a place à amélioration dans votre script, par exemple en ajoutant une -poption à la cpcommande afin que les autorisations et les horodatages soient préservés tout au long de l'opération de dissociation, et vous pouvez ajouter une gestion des erreurs afin que le fichier temporaire soit supprimé en cas d'erreur, mais l'idée de base de votre solution est la seule qui fonctionnera. Pour dissocier un fichier, vous devez le copier, puis déplacer la copie sur le nom d'origine. Il n'y a pas de solution "moins grossière" et cette solution a des conditions de concurrence dans le cas où un autre processus accède au fichier en même temps.

Celada
la source
En effet, j'utilise toujours cp -a lors de la copie de trucs, pour tout conserver, récursivement et copier les liens symboliques en tant que liens symboliques. Je ne sais pas pourquoi je l'ai oublié cette fois, mais après avoir vu votre réponse, j'ai compris que j'avais foiré tous mes horodatages et que je devais (plutôt douloureusement) les récupérer à partir d'une sauvegarde.
Suzanne Dupéron
5

Si vous souhaitez brûler de l'espace disque et que vous disposez d'une version relativement moderne de tar(par exemple, ce qui est sur Ubuntu 10.04 et CentOS 6), vous pouvez jouer avec l' --hard-dereferenceoption.

Quelque chose comme:

$ cd /path/to/directory
$ ls -l *
bar:
total 12
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 2 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

(où j'avais couru ln foo/[12] bar)

$ tar cvf /tmp/dereferencing.tar --hard-dereference .
$ tar xvf /tmp/dereferencing.tar
$ ls -l *
bar:
total 12
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 1 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

Depuis la page de manuel:

   --hard-dereference
          follow hard links; archive and dump the files they refer to
cjc
la source
Je soupçonne qu'il y a peu de goudron qui ne peut pas faire. Belle solution.
Joseph Kern
J'ai oublié de mentionner que je n'avais pas assez d'espace disque pour tout copier. Fondamentalement, votre méthode est la même que cp -a --no-preserve=links /path/to/folder /path/to/copy && rm -rf /path/to/folder && mv /path/to/copy /path/to/foldersi je ne me trompe pas. Je suppose que votre méthode serait plus efficace, car tar impliquerait moins de recherches de disque, donc moins de thrash. On pourrait obtenir la même chose avec rsync, avec des performances encore plus faibles que la méthode cp :).
Suzanne Dupéron
1
Pour éviter d'utiliser trop de disque supplémentaire, il pourrait être possible d'exécuter quelque chose comme, tar cvf - --hard-dereference . | tar xf -mais il pourrait y avoir une condition de concurrence critique qui ferait exploser les choses. Je ne l'ai pas essayé, et je suis peu disposé à le faire pour le moment.
cjc