Convertir un lien symbolique absolu en lien symbolique relatif avec une simple commande Linux

29

J'ai un sous-système de fichiers complet à l' intérieur d' un chemin /home/user/systemcontenant la structure standard Linux avec des répertoires /bin, /home, /root, /usr, /var, /etc, ...

Ce sous-système de fichiers contient des liens symboliques, relatifs ou absolus. Les liens symboliques relatifs sont très bien, ils restent dans le sous-système de fichiers sous /home/user/system. Mais les liens symboliques absolus sont problématiques, car ils pointent vers une cible en dehors du sous-système de fichiers.

À titre d'exemple, nous supposons un lien symbolique absolu comme suit (vu à l'intérieur du sous-système de fichiers):

/usr/file1 -> /usr/lib/file1

Dans le système de fichiers global, nous avons un lien /home/user/system/usr/file1qui pointe maintenant vers un fichier /usr/lib/file1à l'extérieur du sous-système de fichiers, au lieu d'un fichier /home/user/system/usr/lib/file1 à l' intérieur du sous-système de fichiers.

Je voudrais avoir un script simple, de préférence une seule ligne de commande (rsync, chroot, find, ...) qui convertit chaque lien symbolique absolu en un lien relatif.

Dans l'exemple donné, ce lien relatif deviendrait

/usr/file1 -> ../usr/lib/file1
Alex
la source
4
Pour ceux qui souhaitent tenter de résoudre ce Q, voici un utilisateur de 250 000 représentants de la tentative de SO: stackoverflow.com/questions/2564634/… . Il y a plusieurs solutions potentielles dans ce fil, mais chacune a des situations spécifiques qu'elle traite et d'autres qu'elle n'a pas.
slm

Réponses:

20

Avec l' symlinksutilitaire de Mark Lord (offert par de nombreuses distributions; si le vôtre ne l'a pas, construisez-le à partir de la source ):

chroot /home/user/system symlinks -cr .

Alternativement, sur les systèmes qui ont une readlinkcommande et un -lnameprédicat pour find(avertissement: code non testé):

cd /home/user/system &&
find . -lname '/*' -exec ksh -c '
  for link; do
    target=$(readlink "$link")
    link=${link#./}
    root=${link//+([!\/])/..}; root=${root#/}; root=${root%..}
    rm "$link"
    ln -s "$root${target#/}" "$link"
  done
' _ {} +
Gilles 'SO- arrête d'être méchant'
la source
Ce serait une bonne solution! Cependant, que signifie l'expression _ {} +à la fin de la découverte? De plus, j'obtiens une erreur find: paths must precede expression: kshqui ne semble pas avoir de sens (car le chemin précède l'expression ksh).
Alex
@ Alex _est $0pour l'extrait de coquille, et {} +est remplacée par la liste des arguments qui deviennent $1, $2etc. , qui for link; do …passe en boucle sur (il est synonyme for link in "$@"; do …). L'erreur findest due à une faute de frappe (j'ai réussi à taper des guillemets au lieu de guillemets simples autour de l'argument -lname).
Gilles 'SO- arrête d'être méchant'
Maintenant, le script s'exécute, mais pas comme prévu. Peut-être que je n'étais pas assez précis, j'ai mis à jour la question.
Alex
@Alex Quel est le problème? Je pense que le principe est valable, mais j'ai peut-être commis des erreurs de codage. As-tu essayé symlinks? Cela résoudrait votre problème - c'est essentiellement pour cela qu'il a été conçu (avec l'ennui que vous devez l'exécuter en chrooté ( fakechroot devrait faire l'affaire)).
Gilles 'SO- arrête d'être méchant'
1
Je préfère fortement une solution prête à l'emploi, sans avoir besoin d'installer quelque chose de supplémentaire.
Alex
4

Pure bash & coreutils, change les liens symboliques en relatifs sans ../s inutiles dans le chemin:

find . -type l | while read l; do
    target="$(realpath "$l")"
    ln -fs "$(realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target")" "$l"
done

Vous pouvez changer:

  • find .pour find /path/to/directoryconvertir les liens symboliques dans ce répertoire
  • ln -fsà echo ln -fsune course sèche

Explication:

  • target="$(realpath "$l")" - trouve le chemin absolu vers la cible du lien symbolique
  • ln -fs- crée symlink ( -s), forçant ( -f) la réécriture de l'existant
  • realpath -s "$l" - trouve un chemin absolu pour se lier symboliquement
  • dirname "$(realpath -s "$l")" - trouve le chemin absolu vers le répertoire contenant le lien symbolique
  • realpath --relative-to="$(dirname "$(realpath -s "$l")")" "$target" - trouve le chemin de la cible par rapport au lien symbolique, en d'autres termes: convertit l'absolu en chemin relatif
LUMIFAZA
la source
1
Je suggère d'ajouter une ifclause pour ignorer les liens brisés.
fuenfundachtzig
Ça ne marche pas. Sur chaque lien, j'obtiens realpath: <chemin vers le lien symbolique>: aucun fichier ou répertoire
whoKnows il y a
0

Cet extrait de bash devrait le faire. Le premier formulaire imprimera ce qu'il fera; le second le fera. Je voudrais examiner attentivement la sortie car elle est destructrice - la ln -fremplace le lien existant.

TOP=/home/user/system
cd $TOP
find * -type l -print | while read l; do echo ln -sf $TOP$(readlink $l) $l; done
find * -type l -print | while read l; do      ln -sf $TOP$(readlink $l) $l; done
qneill
la source
Outre le fait que cela échouera pour les noms avec des espaces, n'est-ce pas le contraire? Il préfixe le chemin absolu?
fuenfundachtzig
0

Voici une solution pure sh.

cd / home / utilisateur / système &&
trouver . -lname '/ *' |
en lisant l; faire
  echo ln -sf $ (echo $ (echo $ l | sed 's | / [^ /] * | / .. | g') $ (readlink $ l) | sed 's /.....//' ) $ l
fait |
sh
Diomidis Spinellis
la source
0

La transformation absolue en liens relatifs est prise en charge par sshfs qui monte les répertoires distants via ssh.

La commande est: Là

sudo sshfs <remote_user>@<remote_ip_address>:/ /home/<host_user>/mntpoint/ -o transform_symlinks -o allow_other

La commande, en particulier la <placeholders>, doit être adaptée à l'environnement spécifique.

user260694
la source