Comment copier un répertoire de manière récursive en utilisant des liens durs pour chaque fichier

52

Je veux créer une "copie" d'une arborescence de répertoires dans laquelle chaque fichier est un lien physique vers le fichier d'origine

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

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

Voici le résultat attendu, une "copie" de l'arborescence où chaque fichier est un lien physique 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
Gudmundur Orn
la source

Réponses:

50

Sous Linux (plus précisément avec GNU et les busyboximplémentations de cpcelles généralement trouvées sur les systèmes ayant Linux comme noyau) et FreeBSD récente, voici comment:

cp -al dirA dirB

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

Gudmundur Orn
la source
Notez que, comme paxsur FreeBSD, les cp -aliens symboliques ne sont pas en liens durs.
Stéphane Chazelas
Sachez que les liens physiques ne fonctionnent pas sur des montages de systèmes de fichiers distincts.
Dave
24

POSIXly, vous utiliseriez paxen mode lecture + écriture avec l’ -loption:

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

( -peConserve tous les attributs possibles des fichiers (dans ce cas seulement les répertoires) qui sont copiés, comme GNU cpde -afait).

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

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

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

  • à cause d’un bogue, Solaris 10 pax(au moins) ne fonctionne pas -rwlen 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 d'en faire link("dirB/file", "dirB/file")au lieu de link("dirA/file", "dirB/file").
  • sous FreeBSD, paxne crée pas de liens physiques pour les fichiers de type symlink (comportement autorisé par POSIX). De plus, il applique également la substitution aux cibles des liens symboliques (comportement non autorisé par POSIX). Ainsi, par exemple, s'il y a un foo -> AAlien symbolique dans dirA, il deviendra foo -> BAdans dirB.

De plus, si vous voulez faire la même chose, mais avec des chemins de fichiers arbitraires dont le contenu est stocké dans $srcet $dst, il est important de réaliser que pax -rwl -- "$src" "$dst"crée la structure de répertoires complète de $srcinside $dst(qui doit exister et être un répertoire). Par exemple, si $srcest foo/bar, alors, $dst/foo/barest créé.

Si au lieu de cela, vous voulez $dstêtre 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")

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

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

Il est intéressant de noter que paxPOSIX a été créé pour fusionner les fonctionnalités des commandes taret cpio.

cpioest une commande Unix historique (à partir de 1977) par opposition à une invention POSIX, et il existe également une implémentation GNU (pas une pax). Ainsi, même s’il ne s’agit plus d’une commande standard (c’est le cas dans SUSv2), elle reste très courante et comporte un ensemble de fonctionnalités de base sur lesquelles vous pouvez généralement compter.

L'équivalent de pax -rwlserait cpio -pl. Pourtant:

  1. cpio prend la liste du fichier d'entrée sur stdin par opposition aux arguments (délimité par une nouvelle ligne, ce qui signifie que les noms de fichier 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 alimentez la sortie de find( findet cpioont été développés conjointement par les mêmes personnes)).
  3. les métadonnées ne sont pas préservées (certaines cpioimplémentations ont des options pour en conserver, mais rien n’est portable).

Donc avec cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")
Stéphane Chazelas
la source
Semble que -s / A / B / est spécifique à mon exemple. Comment feriez-vous cela si le nom du répertoire source et le nom du répertoire cible étaient des variables $ sourcedir et $ targetdir?
Gudmundur Orn
@GudmundurOrn, voir edit.
Stéphane Chazelas
J'exécute cette commande sur OS X et je ne reçois qu'un message d'erreur "pax: impossible de lier le fichier ./a.txt à lui-même". J'ai utilisé littéralement votre commande, remplaçant simplement le répertoire source par le nom réel, laissant / A / B et le point final tels quels. Est-ce que je comprends mal quelque chose?
db
@db, -s /A/Bremplace Apar Bce qui dirAdevient dirB. Si votre nom de répertoire source n'a pas A, cela le copiera (lien) sur lui-même. Voir aussi le reste de la réponse pour éventuellement de meilleures approches.
Stéphane Chazelas
6

Réponse courte:

cd $source_folder
pax -rwlpe . $dest_folder
lkraider
la source
2

Si vous recherchez cette fonctionnalité de copie avec liens durs pour effectuer des instantanés ou des sauvegardes de (tout ou partie de) vos fichiers, consultez-les rsnapshot.

Janis
la source
1
C'est intéressant. Mais je suppose que les liens physiques ne sont qu'un bon mécanisme de capture instantanée si les fichiers ne seront pas modifiés. Droite?
Gudmundur Orn
@Gudmundur Orn; C'est correct. L'outil mentionné dans ma réponse créera un nouvel instantané de manière à ce que les fichiers soient uniques. les fichiers existants (non modifiés) seront créés en tant que liens durs et les nouveaux fichiers (ou les versions modifiées de fichiers existants) seront créés en tant que nouveaux fichiers. En conséquence, vous aurez le moins de redondance.
Janis
0

La réponse de @ gudmundur-orn est correcte, mais si vous êtes sur BtrFS sous Linux cp a --reflink=auto dirA dirB, faites l'affaire, à la différence que les fichiers sont en réalité différents et que leur modification ne change pas l'autre. Vous pouvez obtenir le même résultat cp -csur un Mac avec APFS ( autoune copie complète -csera impossible si elle échoue).

Tous les systèmes de fichiers COW devraient pouvoir le faire, mais les fournisseurs ne se sont pas mis d’accord sur une option de ligne de commande standard.

Rbanffy
la source