Patrice a identifié la source du problème dans sa réponse , mais si vous voulez savoir comment en arriver à pourquoi vous obtenez cela, voici la longue histoire.
Le répertoire de travail actuel d'un processus n'est pas ce 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 (a) complet de ce répertoire en cours, il lit simplement les entrées du répertoire dans ce fichier répertoire pour trouver le premier composant du chemin relatif (et ..
ressemble à tout autre déposer à cet égard) et continue à partir de là.
En tant qu'utilisateur, vous souhaitez parfois savoir où se trouve ce répertoire dans l'arborescence. Avec la plupart des Unices, l’arborescence de répertoires est une arborescence, sans boucle. C'est-à-dire qu'il n'y a qu'un seul chemin depuis la racine de tree ( /
) vers un fichier donné. Ce chemin s'appelle généralement le chemin canonique.
Pour obtenir le chemin du répertoire de travail actuel, ce processus doit faire est tout simplement marcher (bien vers le bas si vous voulez voir un arbre avec sa racine au fond) l'arbre jusqu'à la racine, trouver les noms des noeuds en chemin.
Par exemple, un processus essayant de savoir que son répertoire actuel est /a/b/c
, ouvrirait le ..
répertoire (le chemin relatif, ainsi que ..
l'entrée dans le répertoire actuel) et rechercherait un fichier de type répertoire avec le même numéro d'inode que .
, découvrez que c
correspond, puis s'ouvre ../..
et ainsi de suite jusqu'à ce qu'il trouve /
. Il n'y a pas d'ambiguïté là-bas.
C’est ce que les fonctions getwd()
ou getcwd()
C font ou ont au moins l'habitude de faire.
Sur certains systèmes tels que Linux moderne, un appel système renvoie le chemin canonique du répertoire actuel qui effectue cette recherche dans l'espace du noyau (et vous permet de rechercher votre répertoire actuel même si vous n'avez pas accès en lecture à tous ses composants). , et c'est ce qui getcwd()
appelle là. Sur Linux moderne, vous pouvez également trouver le chemin du répertoire actuel via un readlink () sur /proc/self/cwd
.
C'est ce que font la plupart des langues et des premiers shells lorsqu'ils renvoient le chemin d'accès au répertoire actuel.
Dans votre cas, vous pouvez appeler cd a
que peut fois que vous voulez, parce que c'est un lien symbolique vers .
le répertoire en cours ne change pas tous 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 cela.
symlinks
autoriser les sauts dans l'arborescence. Dans /a/b/c
, si /a
ou /a/b
ou /a/b/c
est un lien symbolique, le chemin canonique de /a/b/c
serait quelque chose de complètement différent. En particulier, l' ..
entrée /a/b/c
n'est pas nécessairement /a/b
.
Dans le shell Bourne, si vous le faites:
cd /a/b/c
cd ..
Ou même:
cd /a/b/c/..
Il n'y a aucune garantie que vous finirez dans /a/b
.
Juste 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 actuel logique pour contourner ce problème. Les gens s'y sont habitués et POSIX a fini par spécifier ce comportement, ce qui signifie que la plupart des obus le font aussi:
Pour les commandes internes cd
et pwd
intégrées ( et uniquement pour celles-ci (mais également pour popd
/ pushd
sur les shells qui en ont)), le shell conserve sa propre idée du répertoire de travail en cours. Il est stocké dans la $PWD
variable spéciale.
Quand tu fais:
cd c/d
même si c
ou c/d
sont des liens symboliques, tout en $PWD
containes /a/b
, il ajoute c/d
à la fin , donc $PWD
devient /a/b/c/d
. Et quand tu fais:
cd ../e
Au lieu de le faire chdir("../e")
, c'est le cas chdir("/a/b/c/e")
.
Et la pwd
commande ne renvoie que le contenu de la $PWD
variable.
C'est utile dans les shells interactifs , car en pwd
sortie un chemin vers le répertoire courant qui donne des informations sur la façon dont vous y êtes arrivé et aussi longtemps que vous utilisez uniquement ..
dans les arguments pour cd
et pas les autres commandes, il est moins susceptible de vous surprendre, car cd a; cd ..
ou cd a/..
serait généralement vous retourner à l'endroit 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 pourraient arriver, n'importe lequel des composants de $PWD
pourrait être renommé. Le répertoire en cours 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 en cours chaque fois qu'il est appelé en descendant dans l'arborescence afin que ses informations soient toujours exactes, mais pour le répertoire logique implémenté par les shells POSIX, les informations contenues $PWD
peuvent devenir obsolètes. Donc, en courant cd
ou pwd
, certains obus peuvent vouloir se protéger de cela.
Dans ce cas particulier, vous voyez différents comportements avec différentes coques.
Certains, comme ceux qui ksh93
ignorent complètement le problème, renverront des informations incorrectes même après votre appel cd
(et vous ne verrez pas le comportement que vous observez bash
là-bas).
Certains aiment bash
ou zsh
vérifient que $PWD
c'est toujours un chemin d'accès au répertoire courant sur cd
, mais pas sur pwd
.
pdksh vérifie les deux pwd
et cd
(mais sur pwd
, ne met pas à jour $PWD
)
ash
(au moins celui trouvé sur Debian) ne vérifie pas, et quand vous le faites cd a
, en fait cd "$PWD/a"
, donc si le répertoire actuel a changé et $PWD
ne pointe plus vers le répertoire actuel, il ne sera pas changé dans le a
répertoire du répertoire actuel. , mais celui dans $PWD
(et retourne une erreur s'il 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
, des bash
vérifications qui $PWD
pointent toujours vers le répertoire en cours. Pour ce faire, il appelle stat()
la valeur de $PWD
vérifier son numéro d'inode et le comparer à celui de .
.
Mais lorsque la recherche du $PWD
chemin implique la résolution d'un trop grand nombre de liens symboliques, le système stat()
renvoie une erreur. Le shell ne peut donc pas vérifier s'il $PWD
correspond toujours au répertoire actuel. Il le calcule à nouveau avec getcwd()
et le met à jour en $PWD
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 est une protection contre les boucles de liens symboliques. La boucle la plus simple peut être réalisée avec
rm -f a b
ln -s a b
ln -s b a
Sans cette protection, cd a/x
le système devrait trouver où les a
liens, les trouver, b
sont un lien symbolique vers lequel les liens a
se poursuivraient indéfiniment. Le moyen le plus simple de se prémunir contre cela consiste à abandonner après avoir résolu plus d'un nombre arbitraire de liens symboliques.
Revenons maintenant au répertoire de travail actuel logique et pourquoi ce n'est pas une si bonne fonctionnalité. Il est important de réaliser que c'est uniquement pour cd
le shell et non pour d'autres commandes.
Par exemple:
cd -- "$dir" && vi -- "$file"
n'est pas toujours la même chose que:
vi -- "$dir/$file"
C'est pourquoi vous trouverez parfois que les utilisateurs recommandent de toujours utiliser des cd -P
scripts pour éviter toute confusion (vous ne voulez pas que votre logiciel traite un argument de manière ../x
différente des autres commandes simplement parce qu'il est écrit dans un shell plutôt que dans un autre langage).
L' -P
option est de désactiver le répertoire logique de manipulation alors cd -P -- "$var"
ne fait appel chdir()
sur le contenu $var
(sauf quand $var
est -
mais c'est une autre histoire). Et après un cd -P
, $PWD
contiendra un chemin canonique.
Ceci est le résultat d'une limite codée en dur dans la source du noyau Linux; pour empêcher le déni de service, la limite du nombre de liens symboliques imbriqués est de 40 (trouvée dans la
follow_link()
fonction internefs/namei.c
, appelée parnested_symlink()
dans la source du noyau).Vous obtiendrez probablement un comportement similaire (et éventuellement une autre limite que 40) avec d'autres noyaux supportant des liens symboliques.
la source
x%40
plutôt quemax(x,40)
. Je suppose que vous pouvez toujours voir que vous avez changé de répertoire.