web-dev-qa-db-fra.com

Comment copier un répertoire récursivement en utilisant des liens physiques pour chaque fichier

Je veux créer une "copie" d'une arborescence de répertoires où chaque fichier est un lien dur vers le fichier d'origine

Exemple: j'ai une structure de répertoires:

dirA/
dirA/file1
dirA/x/
dirA/x/file2
dirA/y/
dirA/y/file3

Voici le résultat attendu, une "copie" de l'arborescence des répertoires où chaque fichier est un lien dur vers le fichier d'origine:

dirB/            #  normal directory
dirB/file1       #  hardlink to dirA/file1
dirB/x/          #  normal directory
dirB/x/file2     #  hardlink to dirA/x/file2
dirB/y/          #  normal directory
dirB/y/file3     #  hardlink to dirA/y/file3
56
Gudmundur Orn

Sous Linux (plus précisément avec les implémentations GNU et busybox de cp que l'on trouve généralement sur les systèmes qui ont Linux comme noyau) et FreeBSD récent, c'est Comment:

cp -al dirA dirB

Pour une solution plus portable, voir la réponse en utilisant pax et cpio par Stéphane Chazelas

54
Gudmundur Orn

POSIXly, vous utiliseriez pax en mode lecture + écriture avec l'option -l:

pax -rwlpe -s /A/B/ dirA .

(-pe Conserve tous les attributs possibles des fichiers (dans ce cas uniquement les répertoires) qui sont copiés, comme GNU cp's -a Le fait)).

Maintenant, bien que standard, cette commande n'est pas nécessairement très portable.

Tout d'abord, de nombreux systèmes basés sur GNU/Linux n'incluent pas pax par défaut (même s'il s'agit d'un utilitaire POSIX non facultatif).

Ensuite, un certain nombre de bogues et de non-conformités avec quelques implémentations provoquent un certain nombre de problèmes avec ce code.

  • en raison d'un bogue, Solaris 10 pax (au moins) ne fonctionne pas lorsque vous utilisez -rwl en combinaison avec -s. Pour une raison quelconque, il semble qu'il applique la substitution à la fois au chemin d'origine et au chemin copié. Donc ci-dessus, il essaierait de faire de la link("dirB/file", "dirB/file") au lieu de link("dirA/file", "dirB/file").
  • sur FreeBSD, pax ne crée pas de liens physiques pour les fichiers de type symlink (un comportement autorisé par POSIX). Non seulement cela, mais il applique également la substitution aux cibles des liens symboliques (un comportement pas autorisé par POSIX). Par exemple, s'il existe un lien symbolique foo -> AA Dans dirA, il deviendra foo -> BA Dans dirB.

De plus, si vous voulez faire de même mais avec des chemins de fichiers arbitraires dont le contenu est stocké dans $src Et $dst, Il est important de réaliser que pax -rwl -- "$src" "$dst" Crée la structure de répertoire complète de $src À l'intérieur de $dst (Qui doit exister et être un répertoire). Par exemple, si $src Est foo/bar, Alors $dst/foo/bar Est créé.

Si à la place, vous voulez que $dst Soit une copie de $src, Le plus simple est probablement de le faire comme:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && pax -rwlpe . "$absolute_dst")

(qui contournerait également la plupart des problèmes mentionnés ci-dessus mais échouerait si le chemin absolu de $dst se termine par des caractères de nouvelle ligne).

Maintenant, cela n'aidera pas sur les systèmes GNU/Linux où il n'y a pas de pax.

Il est intéressant de noter que pax a été créé par POSIX pour fusionner les fonctionnalités des commandes tar et cpio.

cpio est une commande historique Unix (de 1977) par opposition à une invention POSIX, et il y a également une implémentation GNU (pas un pax one). Donc même si ce n'est plus une commande standard (elle était en SUSv2 cependant), elle est toujours très courante, et il existe un ensemble de fonctionnalités de base sur lesquelles vous pouvez généralement compter. sur.

L'équivalent de pax -rwl Serait cpio -pl. Toutefois:

  1. cpio prend la liste des fichiers d'entrée sur stdin par opposition aux arguments (délimité par des retours à la ligne, ce qui signifie que les noms de fichiers avec des caractères de nouvelle ligne ne sont pas pris en charge)
  2. Tous les fichiers doivent être spécifiés (généralement vous l'alimentez en sortie de find (find et cpio ont été développés conjointement par les mêmes personnes)).
  3. les métadonnées ne sont pas conservées (certaines implémentations cpio ont des options pour en conserver, mais rien de portable).

Donc avec cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")
27

Réponse courte:

cd $source_folder
pax -rwlpe . $dest_folder
6
lkraider

Dans le cas où vous recherchez cette fonctionnalité copie avec liens durs pour créer des instantanés pour les sauvegardes de (tout ou partie de) vos fichiers, consultez rsnapshot.

2
Janis

La réponse de @ gudmundur-orn est correcte, mais si vous êtes sur BtrFS sous Linux cp a --reflink=auto dirA dirB devrait faire l'affaire, à la différence près que les fichiers sont réellement différents et changer l'un ne change pas l'autre. Vous pouvez obtenir la même chose avec cp -c sur un Mac avec APFS (auto fera une copie complète si ce n'est pas possible, -c échouera).

Tout système de fichiers COW devrait être en mesure de le faire, mais les fournisseurs ne se sont pas mis d'accord sur une option de ligne de commande standard.

0
rbanffy