Siempre que tenga permisos de ejecución en el directorio actual, o en el directorio desde el que ejecutó su script de shell, si desea una ruta absoluta a un directorio, todo lo que necesita es cd
.
Paso 10 de cd
las especificaciones
Si la -P
opción está vigente, la $PWD
variable de entorno se establecerá en la cadena que generaría pwd -P
. Si no hay permiso suficiente en el nuevo directorio, o en cualquier padre de ese directorio, para determinar el directorio de trabajo actual, el valor de$PWD
variable de entorno no se especifica.
Y en pwd -P
El nombre de ruta escrito en la salida estándar no debe contener ningún componente que haga referencia a archivos de tipo enlace simbólico. Si hay varios nombres de ruta que elpwd
utilidad podría escribir en la salida estándar, uno que comience con un solo carácter / barra diagonal y uno o más que comiencen con dos caracteres / barra diagonal, entonces escribirá el nombre de ruta que comienza con un solo carácter / barra diagonal. El nombre de ruta no debe contener caracteres innecesarios / barra inclinada después de uno o dos caracteres principales / barra diagonal.
Es porque cd -P
tiene que establecer el directorio de trabajo actual a lo que pwd -P
debería imprimirse y eso cd -
tiene que imprimir lo $OLDPWD
que funciona:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
SALIDA
/home/mikeserv/test/ln
esperalo ...
cd -P . ; cd . ; cd -
SALIDA
/home/mikeserv/test/dir
Y cuando imprimo con cd -
estoy imprimiendo $OLDPWD
. cd
establece $PWD
tan pronto como cd -P .
$PWD
ahora sea una ruta absoluta a /
, por lo que no necesito ninguna otra variable. Y en realidad, ni siquiera debería necesitar el seguimiento, .
pero hay un comportamiento específico de restablecer $PWD
a$HOME
en un shell interactivo cuando cd
es sin adornos. Por lo tanto, es un buen hábito desarrollar.
Entonces, solo hacer lo anterior en la ruta ${0%/*}
debería ser más que suficiente para verificar $0
la ruta, pero en el caso de que$0
sea un enlace suave, probablemente no pueda cambiar el directorio en él, desafortunadamente.
Aquí hay una función que manejará eso:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
Se esfuerza por hacer todo lo que podría en el shell actual, sin invocar un subshell, aunque hay subshells invocados por errores y enlaces blandos que no apuntan a directorios. Depende de un shell compatible con POSIX y de un compatible con POSIX ls
, así como de un dispositivo limpio_function()
espacio de nombres . Todavía funcionará bien sin este último, aunque puede sobrescribir unset
algunas funciones de shell actuales en ese caso. En general, todas estas dependencias deberían estar disponibles de manera bastante confiable en una máquina Unix.
Llamado con o sin argumentos, lo primero que hace es restablecer $PWD
su valor canónico: resuelve los enlaces a sus objetivos según sea necesario. Llamado sin argumentos y eso es todo; pero llamó con ellos y resolverá y canonizará la ruta para cada uno o imprimirá un mensaje parastderr
qué no.
Debido a que opera principalmente en el shell actual, debería poder manejar una lista de argumentos de cualquier longitud. También busca la $_zdlm
variable (que también unset
es cuando está terminada) e imprime su valor de C-escape inmediatamente a la derecha de cada uno de sus argumentos, cada uno de los cuales siempre va seguido de un solo \n
carácter de línea de hilo.
Cambia mucho el directorio, pero, aparte de establecerlo en su valor canónico, no afecta $PWD
, aunque $OLDPWD
no se puede contar de ninguna manera cuando está terminado.
Intenta abandonar cada uno de sus argumentos lo antes posible. Primero intenta cd
entrar $1
. Si puede, imprime la ruta canónica del argumento a stdout
. Si no puede, verifica si $1
existe y no es un enlace suave. Si es cierto, se imprime.
De esta manera, maneja cualquier argumento de tipo de archivo que el shell tenga permisos para abordar, a menos que $1
sea un enlace simbólico que no apunte a un directorio. En ese caso, llama al while
bucle en una subshell.
Llama ls
para leer el enlace. El directorio actual debe cambiarse a su valor inicial primero para poder manejar de manera confiable cualquier ruta de referencia y, por lo tanto, en la subshell de sustitución de comandos, la función hace:
cd -...ls...echo /
Se despoja de la izquierda de ls
la salida de tan poco como debe contener completamente el nombre del enlace y la cadena ->
. Si bien al principio intenté evitar hacer esto shift
y $IFS
resulta que este es el método más confiable tan cerca como puedo imaginar. Esto es lo mismo que hace poor_mans_readlink de Gilles, y está bien hecho.
Repetirá este proceso en un bucle hasta que el nombre de archivo devuelto ls
definitivamente no sea un enlace suave. En ese punto, canonicaliza ese camino como antes con las cd
impresiones.
Ejemplo de uso:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
SALIDA
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
O posiblemente ...
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
SALIDA
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...