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 -Popción está vigente, la $PWDvariable 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 -Ptiene que establecer el directorio de trabajo actual a lo que pwd -Pdebería imprimirse y eso cd -tiene que imprimir lo $OLDPWDque 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. cdestablece $PWDtan pronto como cd -P . $PWDahora 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 $PWDa$HOME en un shell interactivo cuando cdes 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 $0la 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 unsetalgunas 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 $PWDsu 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 $_zdlmvariable (que también unsetes 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 \ncarácter de línea de hilo.
Cambia mucho el directorio, pero, aparte de establecerlo en su valor canónico, no afecta $PWD, aunque $OLDPWDno se puede contar de ninguna manera cuando está terminado.
Intenta abandonar cada uno de sus argumentos lo antes posible. Primero intenta cdentrar $1. Si puede, imprime la ruta canónica del argumento a stdout. Si no puede, verifica si $1existe 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 $1sea un enlace simbólico que no apunte a un directorio. En ese caso, llama al whilebucle en una subshell.
Llama lspara 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 lsla salida de tan poco como debe contener completamente el nombre del enlace y la cadena ->. Si bien al principio intenté evitar hacer esto shifty $IFSresulta 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 lsdefinitivamente no sea un enlace suave. En ese punto, canonicaliza ese camino como antes con las cdimpresiones.
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' -> ...