
Estoy buscando un comando para devolver la ruta absoluta de un archivo, sin resolver enlaces simbólicos. En general, realpath
lo hace bien.
$ mkdir /tmp/test; cd /tmp/test
$ mkdir foo
$ ln -s foo bar
$ realpath -se bar # good, this does not resolve the symlink
/tmp/test/bar
También funciona para archivos dentro de directorios de enlaces simbólicos.
$ touch foo/file
$ realpath -se bar/file # good, this does not resolve the symlink
/tmp/test/bar/file
Sin embargo, fracasa cuando el actual directoresun directorio de enlace simbólico
$ cd bar
$ pwd
/tmp/test/bar
$ realpath -se file # this fails, returning the target
/tmp/test/foo/file
$ realpath -se . # this also fails, returning the target
/tmp/test/foo
$ realpath -se /tmp/test/bar/file # yet this works
/tmp/test/bar/file
$ realpath -se /tmp/test/bar # and this works
/tmp/test/bar
¿Por qué se realpath
comportaría así? (¿Es un error?) ¿Hay alguna manera de evitar realpath
que nunca se resuelva el enlace simbólico, o hay otro método que debería usar?
Respuesta1
El directorio de trabajo actual (CWD) de un proceso se hereda en el nivel del sistema operativo del proceso anterior o se puede cambiar para el proceso actual usando chdir(2)
. El sistema operativo (aquí me refiero a "el núcleo"), por supuesto, siempre resolverá cualquier enlace simbólico para determinar el resultado final, que debe ser un directorio, no un enlace simbólico (a un directorio). Por ejemplo, la llamada al sistema anterior ( chdir(2)
) puede devolver el error ELOOP
cuando había demasiados enlaces simbólicos para resolver. Entonces, desde el punto de vista del sistema operativo, no puede haber un CWD que no sea un directorio para ningún proceso: el sistema operativo siempre lo resolverá en la ruta real sin ningún enlace simbólico en ninguna parte.
Una vez que el shell lo hizo cd /tmp/test/bar
, el sistema operativo resolvió la ruta CWD en /tmp/test/foo
. Por ejemplo, en un sistema Linux, ls -l /proc/$$/cwd
se mostrará el enlace a la ruta resuelta tal como la ve el kernel: /tmp/test/foo
.
El hecho de que el shell todavía se muestre bar
en su mensaje es que debido a que recuerda elcdcomando hecho antes. El comportamiento podría depender del tipo de caparazón. Asumiré bash aquí. Por lo tanto, está integrado (pero no el comando pwd
externo ), la variable y su uso "mentirán" al usuario sobre el directorio actual./bin/pwd
$PWD
$PS1
Cualquier proceso, como realpath
, o /bin/pwd
ejecutado desde el shell, por supuesto, heredará elactualCWD, que es /tmp/test/foo
. Así que eso no es un error realpath
, nunca tendrá información específica sobre él bar
.
Una posible forma incómoda, como sugiere Kusalananda, es reutilizar de alguna manera la $PWD
variable y anteponerla al realpath
argumento solo si su argumento aún no es absoluto.
He aquí un ejemplo. No estoy seguro de que no haya formas de abusar de él. Por ejemplo, si bien la siguiente función funciona, la $PWD
variable en sí no se comporta bien en bash 4.4.12 (Debian 9), pero funciona bien en bash 5.0.3 (Debian 10) si hay un carácter de salto de línea en la ruta. Cuando hay un salto de línea en alguna parte, para que sea útil, -z
también se debe agregar una opción, realpath
pero no voy a volver a implementar todo el análisis de opciones en este ejemplo simple.
myrealpathnofollowsym () {
for p in "$@"; do
if ! printf '%s' "$p" | grep -q -- '^/'; then
realpath -se "$PWD/$p"
else
realpath -se "$p"
fi
done
}