Dada una ruta absoluta o relativa (en un sistema similar a Unix), me gustaría determinar la ruta completa del objetivo después de resolver cualquier enlace simbólico intermedio. Puntos de bonificación por resolver también la ~ notación de nombre de usuario al mismo tiempo.
Si el objetivo es un directorio, podría ser posible chdir () en el directorio y luego llamar a getcwd (), pero realmente quiero hacer esto desde un script de shell en lugar de escribir un asistente de C. Desafortunadamente, los shells tienden a tratar de ocultar la existencia de enlaces simbólicos al usuario (esto es bash en OS X):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
Lo que quiero es una función resolve () tal que cuando se ejecute desde el directorio tmp en el ejemplo anterior, resolve ("foo") == "/ Users / greg / tmp / bar".
cd
hacerlo primero, antes de llamarpwd -P
. En otras palabras: no le permitirá resolver (ver el objetivo de) enlaces simbólicos a archivos o enlaces simbólicos rotos , y para resolver enlaces simbólicos de directorios existentes debe realizar un trabajo adicional (restaurar el directorio de trabajo anterior o localizar las llamadascd
ypwd -P
en una subshell).Nota del editor: lo anterior funciona con GNU
readlink
y FreeBSD / PC-BSD / OpenBSDreadlink
, pero no en OS X a partir de 10.11.GNU
readlink
ofrece opciones adicionales relacionadas, como-m
por ejemplo para resolver un enlace simbólico si existe o no el objetivo final.Tenga en cuenta que desde GNU coreutils 8.15 (2012-01-06), hay un programa realpath disponible que es menos obtuso y más flexible que el anterior. También es compatible con la utilidad FreeBSD del mismo nombre. También incluye funcionalidad para generar una ruta relativa entre dos archivos.
[ Adición de administrador a continuación del comentario de halloleo - danorton]
Para Mac OS X (hasta al menos 10.11.x), use
readlink
sin la-f
opción:Nota del editor: Esto no resolverá los enlaces simbólicos de forma recursiva y, por lo tanto, no informará el objetivo final ; por ejemplo, dado el enlace simbólico
a
que apuntab
, que a su vez apuntac
, esto solo informaráb
(y no garantizará que se muestre como una ruta absoluta ).Use el siguiente
perl
comando en OS X para llenar el vacío de lareadlink -f
funcionalidad que falta :perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"
fuente
readlink
funciona en OSX, pero necesita otra sintaxis:readlink $path
sin el-f
."pwd -P" parece funcionar si solo quieres el directorio, pero si por alguna razón quieres el nombre del ejecutable real, no creo que eso ayude. Aquí está mi solución:
fuente
Uno de mis favoritos es
realpath foo
fuente
realpath
Centos 6 con GNU coreutils 8.4.31. Me he encontrado con muchos otros en Unix y Linux que tienen un núcleo de GNU empaquetado sin élrealpath
. Por lo tanto, parece depender de algo más que una versión.realpath
sobrereadlink
porque ofrece banderas de opciones como--relative-to
parece ser exactamente lo que está pidiendo: acepta una ruta arbitraria, resuelve todos los enlaces simbólicos y devuelve la ruta "real", y es "estándar * nix" que probablemente todos los sistemas ya tengan
fuente
De otra manera:
fuente
Al reunir algunas de las soluciones dadas, sabiendo que readlink está disponible en la mayoría de los sistemas, pero necesita diferentes argumentos, esto funciona bien para mí en OSX y Debian. No estoy seguro acerca de los sistemas BSD. Tal vez la condición debe ser
[[ $OSTYPE != darwin* ]]
excluir-f
solo de OSX.fuente
Así es como se puede obtener la ruta real al archivo en MacOS / Unix usando un script Perl en línea:
Del mismo modo, para obtener el directorio de un archivo con enlace simbólico:
fuente
¿Es su ruta un directorio, o podría ser un archivo? Si es un directorio, es simple:
Sin embargo, si puede ser un archivo, entonces esto no funcionará:
porque el enlace simbólico podría resolverse en una ruta relativa o completa.
En las secuencias de comandos, necesito encontrar la ruta real, para poder hacer referencia a la configuración u otras secuencias de comandos instaladas junto con ella, utilizo esto:
Puede configurar
SOURCE
cualquier ruta de archivo. Básicamente, mientras la ruta sea un enlace simbólico, resuelve ese enlace simbólico. El truco está en la última línea del bucle. Si el enlace simbólico resuelto es absoluto, lo usará comoSOURCE
. Sin embargo, si es relativo, antepondrá elDIR
para ello, que se resolvió en una ubicación real mediante el simple truco que describí por primera vez.fuente
fuente
Nota: Creo que esta es una solución sólida, portátil y lista para usar, que es invariablemente larga por esa misma razón.
A continuación se muestra una secuencia de comandos / función totalmente compatible con POSIX que, por lo tanto, es multiplataforma (también funciona en macOS, que
readlink
aún no es compatible a-f
partir de 10.12 (Sierra)): solo utiliza funciones de lenguaje shell POSIX y solo llamadas de utilidad compatibles con POSIX .Es una implementación portátil de GNU
readlink -e
(la versión más estricta dereadlink -f
).Puede ejecutar la secuencia de comandos con el
sh
o la fuente de la función enbash
,ksh
yzsh
:Por ejemplo, dentro de un script puede usarlo de la siguiente manera para obtener el verdadero directorio de origen del script en ejecución, con los enlaces simbólicos resueltos:
rreadlink
definición de script / función:El código fue adaptado con gratitud por esta respuesta .
También he creado una
bash
versión de utilidad independiente basada en aquí , que puede instalarnpm install rreadlink -g
si tiene Node.js instalado.Una tangente a la seguridad:
jarno , en referencia a la función que garantiza que
command
una función de alias o shell del mismo nombre no esté sombreada por un nombre, pregunta en un comentario:La motivación detrás de
rreadlink
garantizar quecommand
tenga su significado original es usarlo para evitar ( alias ) conveniencia de alias y funciones que a menudo se usan para sombrear comandos estándar en shells interactivos, como redefinirls
para incluir opciones favoritas.Creo que es seguro decir que a menos que usted está tratando con un entorno sin confianza, malicioso, preocuparse
unalias
ounset
- o, para el caso,while
,do
, ... - siendo redefinido no es una preocupación.Hay algo en lo que la función debe confiar para que tenga su significado y comportamiento originales: no hay forma de evitarlo.
El hecho de que los shells similares a POSIX permitan la redefinición de las palabras clave incorporadas e incluso del lenguaje es inherentemente un riesgo de seguridad (y escribir código paranoico es difícil en general).
Para abordar sus inquietudes específicamente:
La función se basa
unalias
yunset
tiene su significado original. Tenerlos redefinidos como funciones de shell de una manera que altere su comportamiento sería un problema; la redefinición como un alias no es necesariamente una preocupación, porque al citar (parte de) el nombre del comando (por ejemplo,\unalias
) se omiten los alias.Sin embargo, citando es no una opción para la cáscara palabras clave (
while
,for
,if
,do
, ...) y aunque las palabras clave shell hacer de tener prioridad sobre la cáscara funciones , enbash
yzsh
alias tienen la prioridad más alta, por lo que para protegerse de las redefiniciones de concha palabra clave que debe ejecutarunalias
con sus nombres (aunque en shells no interactivosbash
(como scripts) los alias no se expanden de forma predeterminada, solo sishopt -s expand_aliases
se llama explícitamente primero).Para asegurarse de que
unalias
, como componente integrado, tenga su significado original,\unset
primero debe usarlo , lo que requiere queunset
tenga su significado original:unset
es un shell integrado , por lo que para asegurarse de que se invoque como tal, deberá asegurarse de que no se redefina como una función . Si bien puede omitir un formulario de alias con comillas, no puede omitir un formulario de función de shell: captura 22.Por lo tanto, a menos que pueda confiar en
unset
que tenga su significado original, por lo que puedo decir, no hay forma garantizada de defenderse contra todas las redefiniciones maliciosas.fuente
[
como un alias endash
ybash
y como una función de shell enbash
.while
como una función enbash
,ksh
yzsh
(pero nodash
), pero solo con lafunction <name>
sintaxis:function while { echo foo; }
funciona (while() { echo foo; }
no). Sin embargo, esto no hará sombra a lawhile
palabra clave , porque las palabras clave tienen mayor prioridad que las funciones (la única forma de invocar esta función es como\while
). Enbash
yzsh
, los alias tienen mayor precedencia que las palabras clave, por lo que las redefiniciones de alias de las palabras clave las sombrean (perobash
de forma predeterminada solo en shells interactivos , a menos queshopt -s expand_aliases
se llame explícitamente).Los scripts de shell comunes a menudo tienen que encontrar su directorio "principal" incluso si se invocan como un enlace simbólico. Por lo tanto, el script debe encontrar su posición "real" desde solo $ 0.
en mi sistema imprime un script que contiene lo siguiente, que debería ser una buena pista de lo que necesita.
fuente
Prueba esto:
fuente
Como me he encontrado con esto muchas veces a lo largo de los años, y esta vez necesitaba una versión portátil puramente bash que pudiera usar en OSX y Linux, seguí adelante y escribí una:
La versión viva vive aquí:
https://github.com/keen99/shell-functions/tree/master/resolve_path
pero por el bien de SO, aquí está la versión actual (creo que está bien probada ... ¡pero estoy abierto a recibir comentarios!)
Puede que no sea difícil hacer que funcione para el bourne shell simple (sh), pero no lo intenté ... Me gusta demasiado $ FUNCNAME. :)
Aquí hay un ejemplo clásico, gracias a Brew:
use esta función y devolverá la ruta -real-:
fuente
Para evitar la incompatibilidad de Mac, se me ocurrió
No es genial, pero cruza el sistema operativo
fuente
python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"
lo que probablemente tendría más sentido. Aún así, para cuando necesite usar Python desde un script de shell, probablemente debería usar Python y ahorrarse el dolor de cabeza de los scripts de shell multiplataforma.dirname
,basename
, yreadlink
son externos utilidades , no cáscara de muebles empotrados;dirname
ybasename
son parte de POSIX,readlink
no lo es.php
viene con OS X. Sin embargo, aunque el cuerpo de la pregunta menciona OS X, no está etiquetado como tal, y queda claro que las personas en varias plataformas vienen aquí para obtener respuestas, por lo que vale la pena señalar qué es específico de la plataforma / no POSIX.Este es un solucionador de enlaces simbólicos en Bash que funciona si el enlace es un directorio o no:
Tenga en cuenta que todos los
cd
yset
las cosas tiene lugar en un subnivel.fuente
{}
alrededores()
no son innecesarios si no hay()
detrás del nombre de la función. Bash acepta declaraciones de funciones sin()
porque el shell no tiene listas de parámetros en las declaraciones y no hace llamadas, por()
lo que las declaraciones de funciones con()
no tienen mucho sentido.Aquí presento lo que creo que es una solución multiplataforma (Linux y macOS al menos) a la respuesta que actualmente funciona bien para mí.
Aquí hay una solución macOS (¿solo?). Posiblemente más adecuado para la pregunta original.
fuente
Mi respuesta aquí Bash: ¿cómo obtener la ruta real de un enlace simbólico?
pero en resumen muy útil en scripts:
Estos son parte de los coreutils de GNU, adecuados para su uso en sistemas Linux.
Para probar todo, ponemos enlace simbólico en / home / test2 /, modificamos algunas cosas adicionales y ejecutamos / llamamos desde el directorio raíz:
Dónde
fuente
En caso de que pwd no pueda usarse (por ejemplo, llamar a scripts desde una ubicación diferente), use realpath (con o sin dirname):
Funciona tanto cuando se llama a través de (múltiples) enlaces simbólicos o cuando se llama directamente al script, desde cualquier ubicación.
fuente