GNU/Linux >> Tutoriels Linux >  >> Linux

Récursivité des liens symboliques - Qu'est-ce qui le rend "réinitialisé" ?

J'ai écrit un petit script bash pour voir ce qui se passe lorsque je continue à suivre un lien symbolique qui pointe vers le même répertoire. Je m'attendais à ce qu'il crée un très long répertoire de travail ou qu'il plante. Mais le résultat m'a surpris…

mkdir a
cd a

ln -s ./. a

for i in `seq 1 1000`
do
  cd a
  pwd
done

Une partie de la sortie est

${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a

que se passe-t-il ici ?

Réponse acceptée :

Patrice a identifié la source du problème dans sa réponse, mais si vous voulez savoir comment aller de là et pourquoi vous obtenez cela, voici la longue histoire.

Le répertoire de travail actuel d'un processus n'est rien que vous penseriez trop compliqué. C'est un attribut du processus qui est un handle vers un fichier de type répertoire d'où partent les chemins relatifs (dans les appels système effectués par le processus). Lors de la résolution d'un chemin relatif, le noyau n'a pas besoin de connaître le chemin complet (a) vers ce répertoire courant, il lit simplement les entrées de répertoire dans ce fichier de répertoire pour trouver le premier composant du chemin relatif (et .. est comme n'importe quel autre fichier à cet égard) et continue à partir de là.

Maintenant, en tant qu'utilisateur, vous aimez parfois savoir où se trouve ce répertoire dans l'arborescence des répertoires. Avec la plupart des Unix, l'arborescence des répertoires est une arborescence, sans boucle. Autrement dit, il n'y a qu'un seul chemin depuis la racine de l'arbre (/ ) à un fichier donné. Ce chemin est généralement appelé chemin canonique.

Pour obtenir le chemin du répertoire de travail actuel, ce qu'un processus doit faire est simplement de monter (enfin descendre si vous aimez voir un arbre avec sa racine en bas) l'arbre revient à la racine, en trouvant les noms des nœuds sur le chemin.

Par exemple, un processus essayant de découvrir que son répertoire courant est /a/b/c , ouvrirait le .. répertoire (chemin relatif, donc .. est l'entrée dans le répertoire courant) et recherchez un fichier de type répertoire avec le même numéro d'inode que . , découvrez que c correspond, puis ouvre ../.. et ainsi de suite jusqu'à ce qu'il trouve / . Il n'y a pas d'ambiguïté.

C'est ce que le getwd() ou getcwd() Les fonctions C font ou du moins faisaient.

Sur certains systèmes comme Linux moderne, il existe un appel système pour renvoyer le chemin canonique vers le répertoire courant qui effectue cette recherche dans l'espace noyau (et vous permet de trouver votre répertoire courant même si vous n'avez pas accès en lecture à tous ses composants) , et c'est ce que getcwd() appelle là-bas. Sur Linux moderne, vous pouvez également trouver le chemin vers le répertoire courant via un readlink() sur /proc/self/cwd .

C'est ce que font la plupart des langages et des premiers shells lorsqu'ils retournent le chemin vers le répertoire courant.

Dans votre cas, vous pouvez appeler cd a autant de fois que vous le souhaitez, car il s'agit d'un lien symbolique vers . , le répertoire courant ne change pas donc tout getcwd() , pwd -P , python -c 'import os; print os.getcwd()' , perl -MPOSIX -le 'print getcwd' renverrait votre ${HOME} .

Maintenant, les liens symboliques ont compliqué tout ça.

symlinks autoriser les sauts dans l'arborescence des répertoires. Dans /a/b/c , si /a ou /a/b ou /a/b/c est un lien symbolique, alors le chemin canonique de /a/b/c serait quelque chose de complètement différent. En particulier, le .. entrée dans /a/b/c n'est pas nécessairement /a/b .

Dans le shell Bourne, si vous le faites :

cd /a/b/c
cd ..

Ou encore :

cd /a/b/c/..

Il n'y a aucune garantie que vous vous retrouverez dans /a/b .

Comme :

vi /a/b/c/../d

n'est pas nécessairement la même chose que :

vi /a/b/d

ksh introduit un concept de répertoire de travail courant logique pour contourner cela d'une manière ou d'une autre. Les gens s'y sont habitués et POSIX a fini par spécifier ce comportement, ce qui signifie que la plupart des shells le font également :

Connexe :Linux – Comprendre la journalisation sous Linux ?

Pour le cd et pwd commandes intégrées (et uniquement pour elles (mais aussi pour popd /pushd sur les shells qui en ont)), le shell conserve sa propre idée du répertoire de travail courant. Il est stocké dans le $PWD variable spéciale.

Lorsque vous faites :

cd c/d

même si c ou c/d sont des liens symboliques, tandis que $PWD contient /a/b , il ajoute c/d à la fin donc $PWD devient /a/b/c/d . Et quand vous le faites :

cd ../e

Au lieu de faire chdir("../e") , il fait chdir("/a/b/c/e") .

Et le pwd la commande ne renvoie que le contenu du $PWD variables.

C'est utile dans les shells interactifs car pwd affiche un chemin vers le répertoire courant qui donne des informations sur la façon dont vous y êtes arrivé et tant que vous n'utilisez que .. en arguments de cd et pas d'autres commandes, il est moins susceptible de vous surprendre, car cd a; cd .. ou cd a/.. vous ramènerait généralement là où vous étiez.

Maintenant, $PWD n'est pas modifié sauf si vous faites un cd . Jusqu'à la prochaine fois que vous appelez cd ou pwd , beaucoup de choses peuvent arriver, n'importe lequel des composants de $PWD pourrait être renommé. Le répertoire courant ne change jamais (c'est toujours le même inode, bien qu'il puisse être supprimé), mais son chemin dans l'arborescence des répertoires peut changer complètement. getcwd() calcule le répertoire courant chaque fois qu'il est appelé en parcourant l'arborescence des répertoires afin que ses informations soient toujours exactes, mais pour le répertoire logique implémenté par les shells POSIX, les informations dans $PWD pourrait devenir obsolète. Donc, lors de l'exécution de cd ou pwd , certains obus voudront peut-être s'en protéger.

Dans ce cas particulier, vous voyez différents comportements avec différents shells.

Certains comme ksh93 ignorez complètement le problème, il renverra donc des informations incorrectes même après avoir appelé cd (et vous ne verriez pas le comportement que vous voyez avec bash là).

Certains comme bash ou zsh vérifiez que $PWD est toujours un chemin vers le répertoire courant sur cd , mais pas sur pwd .

pdksh vérifie à la fois pwd et cd (mais sur pwd , ne met pas à jour $PWD )

ash (du moins celui trouvé sur Debian) ne vérifie pas, et quand vous faites cd a , il fait en fait cd "$PWD/a" , donc si le répertoire courant a changé et $PWD ne pointe plus vers le répertoire courant, il ne changera en fait pas le a répertoire dans le répertoire courant, mais celui dans $PWD (et renvoie une erreur si elle n'existe pas).

Si vous voulez jouer avec, vous pouvez faire :

cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b 
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)

dans divers coquillages.

Dans votre cas, puisque vous utilisez bash , après un cd a , bash vérifie que $PWD pointe toujours vers le répertoire courant. Pour ce faire, il appelle stat() sur la valeur de $PWD pour vérifier son numéro d'inode et le comparer avec celui de . .

Mais lorsque la recherche du $PWD path implique la résolution de trop de liens symboliques, que stat() retourne avec une erreur, donc le shell ne peut pas vérifier si $PWD correspond toujours au répertoire courant, il le recalcule donc avec getcwd() et met à jour $PWD en conséquence.

Maintenant, pour clarifier la réponse de Patrice, cette vérification du nombre de liens symboliques rencontrés lors de la recherche d'un chemin consiste à se prémunir contre les boucles de liens symboliques. La boucle la plus simple peut être faite avec

rm -f a b
ln -s a b
ln -s b a

Sans ce garde-fou, sur un cd a/x , le système devrait trouver où a liens vers, trouve que c'est b et est un lien symbolique qui renvoie à a , et cela durerait indéfiniment. Le moyen le plus simple de s'en prémunir est d'abandonner après avoir résolu plus qu'un nombre arbitraire de liens symboliques.

En relation :Steam – Les mods sont-ils appliqués sur plusieurs ordinateurs lorsque je associe le compte Nexus au compte Steam ?

Revenons maintenant au répertoire de travail courant logique et pourquoi ce n'est pas une si bonne fonctionnalité. Il est important de réaliser que c'est uniquement pour cd dans le shell et non dans d'autres commandes.

Par exemple :

cd -- "$dir" &&  vi -- "$file"

n'est pas toujours le même que :

vi -- "$dir/$file"

C'est pourquoi vous constaterez parfois que les gens recommandent de toujours utiliser cd -P dans les scripts pour éviter toute confusion (vous ne voulez pas que votre logiciel gère un argument de ../x différemment des autres commandes simplement parce qu'il est écrit en shell au lieu d'un autre langage).

Le -P l'option est de désactiver le répertoire logique gérer donc cd -P -- "$var" appelle en fait chdir() sur le contenu de $var (au moins tant que $CDPATH il n'est pas défini, et sauf lorsque $var est - (ou éventuellement -2 , +3 … dans certains coquillages) mais c'est une autre histoire). Et après un cd -P , $PWD contiendra un chemin canonique.


Linux
  1. Créer un nouveau répertoire en C

  2. Pourquoi mon lien symbolique crée-t-il un fichier et non un dossier ?

  3. Ajouter un script bash au chemin

  4. Comment supprimer un lien symbolique vers un répertoire ?

  5. Que se passe-t-il si mv est interrompu ?

Qu'est-ce que les liens symboliques sous Linux ? Comment créer des liens symboliques ?

Chemin absolu ou relatif sous Linux :quelle est la différence ?

Linux :ajouter un répertoire à PATH

Utilisation de / lors de l'utilisation d'un cd

Créer un programme dans bin

Que se passe-t-il si je supprime perdu+trouvé