Patrice identificó la fuente del problema en su respuesta , pero si desea saber cómo llegar desde allí y por qué lo obtiene, aquí está la larga historia.
El directorio de trabajo actual de un proceso no es nada que pueda parecer demasiado complicado. Es un atributo del proceso que es un identificador de un archivo de directorio de tipo desde donde comienzan las rutas relativas (en las llamadas al sistema realizadas por el proceso). Al resolver una ruta relativa, el núcleo no necesita conocer la (a) ruta completa a ese directorio actual, solo lee las entradas del directorio en ese archivo de directorio para encontrar el primer componente de la ruta relativa (y ..
es como cualquier otro archivo en ese sentido) y continúa desde allí.
Ahora, como usuario, a veces le gusta saber dónde se encuentra ese directorio en el árbol de directorios. Con la mayoría de los Unices, el árbol de directorios es un árbol, sin bucle. Es decir, solo hay una ruta desde la raíz del árbol ( /
) a cualquier archivo dado. Ese camino generalmente se llama el camino canónico.
Para obtener la ruta del directorio de trabajo actual, lo que un proceso tiene que hacer es caminar hacia arriba (bien hacia abajo si desea ver un árbol con su raíz en la parte inferior) el árbol de regreso a la raíz, encontrando los nombres de los nodos en camino.
Por ejemplo, un proceso que intenta descubrir que su directorio actual es /a/b/c
, abriría el ..
directorio (ruta relativa, también lo ..
es la entrada en el directorio actual) y buscaría un archivo de tipo directorio con el mismo número de inodo que .
, descubra que c
coincide, luego se abre ../..
y así sucesivamente hasta que encuentra /
. No hay ambigüedad allí.
Eso es lo que las funciones getwd()
o getcwd()
C hacen o al menos solían hacer.
En algunos sistemas como Linux moderno, hay una llamada al sistema para devolver la ruta canónica al directorio actual que realiza esa búsqueda en el espacio del kernel (y le permite encontrar su directorio actual incluso si no tiene acceso de lectura a todos sus componentes) , y eso es lo que getcwd()
llama allí. En Linux moderno, también puede encontrar la ruta al directorio actual a través de un enlace de lectura () en /proc/self/cwd
.
Eso es lo que hacen la mayoría de los idiomas y los primeros shells al devolver la ruta al directorio actual.
En su caso, se le puede llamar cd a
como mayo veces como quiera, porque es un enlace simbólico a .
, el directorio actual no cambia por lo que todos getcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, perl -MPOSIX -le 'print getcwd'
volvería a su ${HOME}
.
Ahora, los enlaces simbólicos complicaron todo eso.
symlinks
permitir saltos en el árbol de directorios. En /a/b/c
, si /a
o /a/b
o /a/b/c
es un enlace simbólico, entonces la ruta canónica de /a/b/c
sería algo completamente diferente. En particular, la ..
entrada /a/b/c
no es necesariamente /a/b
.
En el shell Bourne, si lo haces:
cd /a/b/c
cd ..
O incluso:
cd /a/b/c/..
No hay garantía de que termines /a/b
.
Al igual que:
vi /a/b/c/../d
no es necesariamente lo mismo que:
vi /a/b/d
ksh
introdujo un concepto de un directorio de trabajo lógico actual para evitarlo de alguna manera. La gente se acostumbró y POSIX terminó especificando ese comportamiento, lo que significa que la mayoría de los proyectiles actuales también lo hacen:
Para los comandos cd
y pwd
builtin ( y solo para ellos (aunque también para popd
/ pushd
en shells que los tienen)), el shell mantiene su propia idea del directorio de trabajo actual. Se almacena en la $PWD
variable especial.
Cuando tu lo hagas:
cd c/d
incluso si c
o c/d
son enlaces simbólicos, mientras $PWD
contiene /a/b
, se agrega c/d
al final así se $PWD
convierte /a/b/c/d
. Y cuando lo haces:
cd ../e
En lugar de hacerlo chdir("../e")
, lo hace chdir("/a/b/c/e")
.
Y el pwd
comando solo devuelve el contenido de la $PWD
variable.
Eso es útil en shells interactivos porque pwd
genera una ruta al directorio actual que brinda información sobre cómo llegó allí y siempre que solo use ..
argumentos cd
y no otros comandos, es menos probable que lo sorprenda, porque cd a; cd ..
o cd a/..
generalmente lo recuperaría a donde estabas
Ahora, $PWD
no se modifica a menos que haga una cd
. Hasta la próxima vez que llame cd
o pwd
, podrían suceder muchas cosas, cualquiera de los componentes de $PWD
podría cambiar su nombre. El directorio actual nunca cambia (siempre es el mismo inodo, aunque podría eliminarse), pero su ruta en el árbol de directorios podría cambiar por completo. getcwd()
calcula el directorio actual cada vez que se llama caminando por el árbol de directorios para que su información sea siempre precisa, pero para el directorio lógico implementado por los shells POSIX, la información $PWD
puede volverse obsoleta. Entonces, al correr cd
o pwd
, algunos proyectiles pueden querer protegerse contra eso.
En ese caso particular, ve diferentes comportamientos con diferentes shells.
A algunos les gusta ksh93
ignorar el problema por completo, por lo que devolverá información incorrecta incluso después de llamar cd
(y no vería el comportamiento que está viendo bash
allí).
A algunos les gusta bash
o zsh
comprueban que $PWD
todavía hay una ruta hacia el directorio actual cd
, pero no sobre pwd
.
pdksh comprueba ambos pwd
y cd
(pero pwd
no actualiza $PWD
)
ash
(al menos el que se encuentra en Debian) no marca, y cuando lo hace cd a
, en realidad lo hace cd "$PWD/a"
, por lo que si el directorio actual ha cambiado y $PWD
ya no apunta al directorio actual, en realidad no cambiará al a
directorio en el directorio actual , pero el que está dentro $PWD
(y devuelve un error si no existe).
Si quieres jugar con él, puedes hacer:
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)
en varias conchas.
En su caso, dado que está utilizando bash
, después de un cd a
, bash
verificaciones que $PWD
todavía apuntan al directorio actual. Para hacer eso, invoca stat()
el valor de $PWD
para verificar su número de inodo y compararlo con el de .
.
Pero cuando la búsqueda de la $PWD
ruta implica resolver demasiados enlaces simbólicos, eso stat()
devuelve un error, por lo que el shell no puede verificar si $PWD
aún corresponde al directorio actual, por lo que lo calcula nuevamente getcwd()
y se actualiza en $PWD
consecuencia.
Ahora, para aclarar la respuesta de Patrice, esa verificación de la cantidad de enlaces simbólicos encontrados al buscar un camino es evitar los bucles de enlaces simbólicos. El bucle más simple se puede hacer con
rm -f a b
ln -s a b
ln -s b a
Sin esa protección segura cd a/x
, el sistema tendría que encontrar dónde se a
vincula, encontrarlo b
y es un enlace simbólico al que se vincula a
, y eso continuaría indefinidamente. La forma más sencilla de protegerse contra eso es darse por vencido después de resolver más de un número arbitrario de enlaces simbólicos.
Ahora regrese al directorio de trabajo lógico actual y por qué no es una característica tan buena. Es importante darse cuenta de que es solo para cd
el shell y no para otros comandos.
Por ejemplo:
cd -- "$dir" && vi -- "$file"
no siempre es lo mismo que:
vi -- "$dir/$file"
Es por eso que a veces encontrarás que las personas recomiendan usar siempre cd -P
en scripts para evitar confusiones (no quieres que tu software maneje un argumento de manera ../x
diferente a otros comandos solo porque está escrito en shell en lugar de otro idioma).
La -P
opción es deshabilitar el manejo del directorio lógico, por lo que en cd -P -- "$var"
realidad invoca chdir()
el contenido de $var
(excepto cuando $var
es -
pero esa es otra historia). Y después de un cd -P
, $PWD
contendrá un camino canónico.
Este es el resultado de un límite codificado en la fuente del kernel de Linux; Para evitar la denegación de servicio, el límite en el número de enlaces simbólicos anidados es 40 (que se encuentra en la
follow_link()
función internafs/namei.c
, llamadanested_symlink()
en la fuente del núcleo).Probablemente obtendría un comportamiento similar (y posiblemente otro límite que 40) con otros núcleos que admiten enlaces simbólicos.
fuente
x%40
lugar demax(x,40)
. Supongo que todavía puedes ver que has cambiado de directorio.