Remplacer les liens symboliques par des fichiers

31

Existe-t-il un moyen simple de remplacer tous les liens symboliques par le fichier auquel ils sont liés?

Marty Trenouth
la source
3
Quel problème essayez-vous de résoudre?
Daniel Beck
3
J'ai un tas de liens symboliques vers un autre lecteur. J'ai besoin de déplacer tous les fichiers de ce lecteur qui sont référencés ailleurs afin de pouvoir le supprimer.
Marty Trenouth

Réponses:

14

Pour certaines définitions de «facile»:

#!/bin/sh
set -e
for link; do
    test -h "$link" || continue

    dir=$(dirname "$link")
    reltarget=$(readlink "$link")
    case $reltarget in
        /*) abstarget=$reltarget;;
        *)  abstarget=$dir/$reltarget;;
    esac

    rm -fv "$link"
    cp -afv "$abstarget" "$link" || {
        # on failure, restore the symlink
        rm -rfv "$link"
        ln -sfv "$reltarget" "$link"
    }
done

Exécutez ce script avec des noms de liens comme arguments, par exemple via find . -type l -exec /path/tos/script {} +

grawity
la source
2
Merci pour l'édition, Anon, mais le script ne les noms de fichiers de poignée avec des espaces très bien, en citant des variables dans tous les endroits nécessaires. Le passage "$var"à "${var}"est un noop dans sh / bash. J'ai supprimé le bashism ( [[), le reste est compatible avec POSIX sh.
grawity
Une méthode encore plus robuste pour gérer les échecs des liens symboliques consiste à placer cpun fichier temporaire dans le même répertoire, puis mv -fce fichier temporaire dans l'original. Cela devrait se prémunir contre des problèmes tels que les personnes qui maintiennent la pression Ctrl-Cou le remplissage du système de fichiers entre les commandes rmet cp(empêchant ainsi même les nouveaux lnde fonctionner). Les noms de fichiers temporaires sont faciles à repérer ls -apour un nettoyage ultérieur si nécessaire.
M. Fooz
En remarque, je ne me souviens vraiment pas pourquoi je n'ai pas simplement utilisé à la abstarget=$(readlink -f "$link")place du bloc de cas , mais cela pourrait avoir été des raisons de portabilité.
grawity
17

Il pourrait être plus facile d'utiliser simplement tar pour copier les données dans un nouveau répertoire.

-H      (c and r mode only) Symbolic links named on the command line will
        be followed; the target of the link will be archived, not the
        link itself.

Vous pouvez utiliser quelque chose comme ça

tar -hcf - sourcedir | tar -xf - -C newdir

tar --help:
-H, --format=FORMAT        create archive of the given format
-h, --dereference          follow symlinks; archive and dump the files they point to
Steve
la source
1
J'adore votre solution, mais je ne comprends pas la dernière partie de votre message. Pour moi, vous devriez faire:tar -hcf - sourcedir | tar -xf - -C newdir
Seb DA ROCHA
1
D'accord, cette réponse devrait être mise à jour conformément au commentaire.
astabada
1
cp -Lest la solution la plus simple
plesiv
@plesiv true et compatible posix, mais toutes les cpimplémentations ne copient pas les répertoires.
Wyatt8740
16

Si je vous ai bien compris, le -Ldrapeau de la cpcommande devrait faire exactement ce que vous voulez.

Copiez simplement tous les liens symboliques et il les remplacera par les fichiers vers lesquels ils pointent.

RedX
la source
4
Merci - presque ce dont j'avais besoin: je devais faire quelque chose comme: cp -L files tmp/ && rm files && cp tmp/files . Si vous clarifiez, vous aiderez probablement plus de gens ...
sage
5

«facile» dépendra très probablement de vous.

J'écrirais probablement un script qui utilise l'utilitaire de ligne de commande "find" pour trouver des fichiers liés symboliquement, puis appelle rm et cp pour supprimer et remplacer le fichier. Vous feriez probablement bien d'avoir également l'action appelée par find check qu'il reste suffisamment d'espace libre avant de déplacer le lien sym également.

Une autre solution peut être de monter le système de fichiers en question à travers quelque chose qui cache les liens sym (comme samba), puis de tout copier. Mais dans de nombreux cas, quelque chose comme ça introduirait d'autres problèmes.

Une réponse plus directe à votre question est probablement «oui».

Modifier: selon la demande d'informations plus spécifiques. Selon la page de manuel de find , cette commande répertorie tous les fichiers de liens sym dans une profondeur de 2 répertoires, à partir de /:

find / -maxdepth 2 -type l -print

( cela a été trouvé ici )

Pour que find exécute quelque chose en le trouvant:

find / -maxdepth 2 -type l -exec ./ReplaceSymLink.sh {} \;

Je crois que cela appellera un script que je viens de créer et transmettra le nom de fichier que vous venez de trouver. Alternativement, vous pouvez capturer la sortie de recherche dans un fichier (utilisez "find [blah]> symlinks.data", etc.), puis passez ce fichier dans un script que vous avez écrit pour gérer la copie gracieuse de l'original.

James T Snell
la source
? Un peu générique - pouvez-vous élaborer?
Linker3000
2
La syntaxe est-exec ./ReplaceSymLink.sh {} \;
grawity
C'est aller chercher les articles et celui marqué comme réponse est utilisé pour remplacer les liens!
Marty Trenouth
3

Voici le modèle que j'ai utilisé: supposez que tous les fichiers locaux sont des liens vers un autre fichier dans "source". Vous pouvez affiner la sélection de fichiers avec des modèles ou "rechercher". Veuillez comprendre avant d'utiliser.

pour f dans *; do cp --remove-destination source / $ f $ f; terminé

J'espère que cela t'aides

MastroGeppetto
la source
que diriez-vous d'utiliser readlink: pour f dans *; do cp ---- remove-destination "$ (readlink $ f)" "$ f"; fait
Diego
Oui, c'est une bonne idée, mais vous perdez un certain contrôle sur l'opération. Une limite de ma solution est qu'elle ne fonctionne pas pour les fichiers avec des blancs au milieu, ou pour des listes très longues. La solution finale est probablement de combiner le 'find --exec' ci-dessus avec le 'remove destination'. Quelqu'un veut essayer?
MastroGeppetto
les blancs au milieu devraient être atténués en mettant "$ f" entre guillemets. Voici comment je l'utilise avec "find & xargs" au lieu de "for loop": find ./ -type l -print0 | xargs -0 -n1 -i sh -c 'cp --remove-destination $ (readlink "{ } ")" {} "'Je l'ai posté comme réponse distincte pour la visibilité. superuser.com/a/1329543/499386
Diego
2
find . -type l -exec cp --dereference --recursive '{}' '{}'.dereferenced \;

Fera une copie de chaque fichier / dossier avec un lien symbolique <filename>.dereferenced, ce qui est plus sûr (si moins pratique) que de les remplacer directement. Le déplacement des données copiées vers les noms de fichiers des liens symboliques est laissé comme exercice au lecteur.

blahdiblah
la source
2

En utilisant certaines des idées précédentes, la commande suivante remplacera récursivement chaque lien symbolique par une copie de l'original:

find . -type l -exec bash -c "echo 'Replacing {} ...';  cp -LR '{}' '{}'.dereferenced;  rm '{}';  mv '{}'.dereferenced '{}'" \;
Antonio
la source
2
find ./ -type l -print0|xargs -0 -n1 -i sh -c 'cp --remove-destination $(readlink "{}") "{}" '

basé sur /superuser//a/1301199/499386 de MastroGeppetto

Diego
la source
1

Beaucoup de bonnes réponses là-bas, mais depuis que vous cherchiez quelque chose de facile

for i in *; do link=$(readlink $i) && rm $i && mv $link $i; done
xaocon
la source
Une petite amélioration aidera à trouver des liens symboliques: pour lnk dans `find. -type l`; do src = $ (readlink $ lnk) && rm $ lnk && mv $ src $ lnk; fait (non testé avec la hiérarchie cependant, soyez prudent avec les contre-coups)
Roman Susi
1

J'ai eu le même problème avec gwenview lors de la copie de liens alors que vous vous attendriez normalement à ce qu'il suive le lien et copie le fichier.

Ce que j'ai fait pour «nettoyer» le répertoire en suivant tous les liens et en copiant tous les fichiers pointés dans le répertoire, a été de créer un répertoire de travail, exécutez par exemple

"for file in `ls`; do cp -L $file scratchdir/$file; done; mv scratchdir/* ./" 

c'est trop dangereux de mettre dans un script car il n'y a pas de vérification mais cela a résolu mon problème.

user225929
la source
1
for f in *; do cp --remove-destination $(readlink "$f") "$f"; done
Lorem Ipsum Dolor
la source
0

J'ai fait une version en ligne, car mon hôtel Web n'autorisait pas les scripts bash et cela a bien fonctionné pour moi:

before:
> find . -type l | wc -l
358

> for link in `find . -type l` ; do echo "$link : "; ls -al $link ; cp $link notalink; unlink $link; mv notalink $link ; ls -al $link; done
...

after:
> find . -type l | wc -l
0
Autotrader
la source