macos - the - ¿Cómo puedo obtener el comportamiento de readlink-f de GNU en una Mac?
how to know the shell in linux (20)
- Instalar homebrew
- Ejecutar "brew install coreutils"
- Ejecutar "greadlink -f ruta"
greadlink es el enlace de lectura gnu que implementa -f. Puedes usar macports u otros también, prefiero homebrew.
En Linux, la utilidad de enlace de readlink
acepta una opción -f
que sigue a enlaces adicionales. Esto no parece funcionar en Mac y posiblemente en sistemas basados en BSD. ¿Cuál sería el equivalente?
Aquí hay algo de información de depuración:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
Aquí hay una función de shell portátil que debería funcionar en CUALQUIER shell comparable de Bourne. Se resolverá la puntuación relativa de la ruta ".. o". y desreferencia enlaces simbólicos.
Si, por alguna razón, no tiene un comando realpath (1), o enlace de lectura (1), puede tener un alias.
which realpath || alias realpath=''real_path''
Disfrutar:
real_path () {
OIFS=$IFS
IFS=''/''
for I in $1
do
# Resolve relative path punctuation.
if [ "$I" = "." ] || [ -z "$I" ]
then continue
elif [ "$I" = ".." ]
then FOO="${FOO%%/${FOO##*/}}"
continue
else FOO="${FOO}/${I}"
fi
## Resolve symbolic links
if [ -h "$FOO" ]
then
IFS=$OIFS
set `ls -l "$FOO"`
while shift ;
do
if [ "$1" = "->" ]
then FOO=$2
shift $#
break
fi
done
IFS=''/''
fi
done
IFS=$OIFS
echo "$FOO"
}
También, en caso de que a alguien le interese aquí, es cómo implementar basename y dirname en código de shell 100% puro:
## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
echo "${1%/*}"
}
## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
echo "${1##*/}"
}
Puede encontrar la versión actualizada de este código de shell en mi sitio de Google: http://sites.google.com/site/jdisnard/realpath
EDITAR: Este código se otorga bajo los términos de la licencia de 2 cláusulas (estilo freeBSD). Puede encontrar una copia de la licencia siguiendo el hipervínculo anterior a mi sitio.
Escribí una utilidad realpath para OS X que puede proporcionar los mismos resultados que readlink -f
.
Aquí hay un ejemplo:
(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11 8月 25 19:29 a -> /etc/passwd
(jalcazar@mac tmp)$ realpath a
/etc/passwd
Si está utilizando MacPorts, puede instalarlo con el siguiente comando: sudo port selfupdate && sudo port install realpath
.
Esto es lo que uso:
stat -f %N $your_path
Hice un script llamado realpath personalmente que se parece a algo como:
#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
La forma más fácil de resolver este problema y habilitar la funcionalidad de readlink en Mac con Homebrew instalado o FreeBSD es instalar el paquete ''coreutils''. También puede ser necesario en ciertas distribuciones de Linux y otros sistemas operativos POSIX.
Por ejemplo, en FreeBSD 11, instalé invocando:
# pkg install coreutils
En MacOS con Homebrew, el comando sería:
$ brew install coreutils
No estoy seguro de por qué las otras respuestas son tan complicadas, eso es todo lo que hay que hacer. Los archivos no están en un lugar diferente, simplemente no están instalados todavía.
La respuesta de @Keith Smith da un bucle infinito.
Aquí está mi respuesta, la cual uso solo en SunOS (SunOS pierde tanto los comandos POSIX y GNU).
Es un archivo de secuencia de comandos que debe colocar en uno de sus directorios de $ PATH:
#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99
link="$1"
while [ -L "$link" ]; do
lastLink="$link"
link=$(/bin/ls -ldq "$link")
link="${link##* -> }"
link=$(realpath "$link")
[ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done
echo "$link"
Las rutas a readlink son diferentes entre mi sistema y el tuyo. Por favor, intente especificar la ruta completa:
/sw/sbin/readlink -f
MacPorts y Homebrew proporcionan un paquete coreutils que contiene greadlink
( greadlink
GNU). Crédito para Michael Kallweitt puesto en mackb.com.
brew install coreutils
greadlink -f file.txt
Mejor tarde que nunca, supongo. Estaba motivado a desarrollar esto específicamente porque mis scripts de Fedora no estaban trabajando en la Mac. El problema son las dependencias y Bash. Las Mac no las tienen, o si las tienen, a menudo están en otro lugar (otra ruta). La manipulación de la ruta de dependencia en un script Bash multiplataforma es, en el mejor de los casos, un dolor de cabeza y, en el peor, un riesgo para la seguridad, por lo que es mejor evitar su uso, si es posible.
La función get_realpath () a continuación es simple, centrada en Bash, y no se requieren dependencias. Yo solo uso Bash builtins echo y cd . También es bastante seguro, ya que todo se prueba en cada etapa del camino y devuelve condiciones de error.
Si no desea seguir los enlaces simbólicos, coloque set -P al principio del script, pero de lo contrario, cd debería resolver los enlaces simbólicos de forma predeterminada. Se ha probado con argumentos de archivo que son {absolutos | pariente enlace simbólico | local} y devuelve la ruta absoluta al archivo. Hasta ahora no hemos tenido ningún problema con ello.
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try ''cd .; cd -;'' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
Puede combinar esto con otras funciones get_dirname, get_filename, get_stemname y validate_path. Estos se pueden encontrar en nuestro repositorio de GitHub como realpath-lib (divulgación completa: este es nuestro producto, pero lo ofrecemos de forma gratuita a la comunidad sin ninguna restricción). También podría servir como una herramienta de instrucción, está bien documentada.
Hemos hecho todo lo posible para aplicar las llamadas prácticas ''Bash modernas'', pero Bash es un tema importante y estoy seguro de que siempre habrá espacio para mejorar. Requiere Bash 4+, pero se podría hacer que funcione con versiones anteriores si aún están disponibles.
Odio acumularme con otra implementación más, pero necesitaba a) una implementación de shell pura y portátil, yb) cobertura de prueba unitaria , ya que la cantidad de casos de borde para algo como esto no es trivial .
Ver mi proyecto en Github para pruebas y código completo. Lo que sigue es una sinopsis de la implementación:
Como Keith Smith señala astutamente, readlink -f
hace dos cosas: 1) resuelve enlaces simbólicos recursivamente, y 2) canonicaliza el resultado, por lo tanto:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
Primero, la implementación del programa de enlace de enlaces:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf ''%s/n'' "$1"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf ''%s/n'' "$2" ;;
* ) printf ''%s/n'' "$1/$2" ;;
esac
}
Tenga en cuenta que esta es una versión ligeramente simplificada de la implementación completa . La implementación completa agrega una pequeña comprobación de los ciclos de enlaces simbólicos , así como un masaje de la salida un poco.
Finalmente, la función para canonicalizar un camino:
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf ''%s/%s/n'' "$(pwd -P)" "$file")
}
Eso es, más o menos. Lo suficientemente simple como para pegarlo en su script, pero lo suficientemente complicado como para estar loco y confiar en cualquier código que no tenga pruebas unitarias para sus casos de uso.
Perl tiene una función de enlace de lectura (por ejemplo, ¿Cómo copio enlaces simbólicos en Perl? ). Esto funciona en la mayoría de las plataformas, incluido OS X:
perl -e "print readlink ''/path/to/link''"
Por ejemplo:
$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink ''x''"
a/b/c
Puede estar interesado en realpath(3)
, o en os.path.realpath
de Python. Los dos no son exactamente iguales; la llamada a la biblioteca de C requiere que existan componentes de ruta intermedios, mientras que la versión de Python no existe.
$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r-- 1 miles wheel 0 Jul 11 21:08 a
lrwxr-xr-x 1 miles wheel 1 Jul 11 20:49 b -> a
lrwxr-xr-x 1 miles wheel 1 Jul 11 20:49 c -> b
$ python -c ''import os,sys;print(os.path.realpath(sys.argv[1]))'' c
/private/tmp/foo/a
Sé que dijiste que preferirías algo más ligero que otro lenguaje de scripting, pero en caso de que compilar un binario sea insoportable, puedes usar Python y ctypes (disponibles en Mac OS X 10.5) para ajustar la llamada de la biblioteca:
#!/usr/bin/python
import ctypes, sys
libc = ctypes.CDLL(''libc.dylib'')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p
def realpath(path):
buffer = ctypes.create_string_buffer(1024) # PATH_MAX
if libc.realpath(path, buffer):
return buffer.value
else:
errno = libc.__error().contents.value
raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))
if __name__ == ''__main__'':
print realpath(sys.argv[1])
Irónicamente, la versión C de este script debería ser más corta. :)
Que hay de esto
function readlink() {
DIR=$(echo "${1%/*}")
(cd "$DIR" && echo "$(pwd -P)")
}
Un simple one-liner en perl que seguramente funcionará en casi todas partes sin dependencias externas:
perl -MCwd -e ''print Cwd::abs_path shift'' ~/non-absolute/file
Dereference symlinks.
El uso en un script podría ser así:
readlinkf(){ perl -MCwd -e ''print Cwd::abs_path shift'' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"
Verdaderamente la plataforma independiente sería también este R-onliner
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
Para imitar realmente el enlace de readlink -f <path>
, sería necesario usar $ 2 en lugar de $ 1.
Ya hay muchas respuestas, pero ninguna funcionó para mí ... Así que esto es lo que estoy usando ahora.
readlink_f() {
local target="$1"
[ -f "$target" ] || return 1 #no nofile
while [ -L "$target" ]; do
target="$(readlink "$target")"
done
echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}
FreeBSD y OSX tienen una versión de stat
derivada de NetBSD.
Puede ajustar la salida con modificadores de formato (consulte las páginas de manual en los enlaces anteriores).
% cd /service
% ls -tal
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------ 3 root wheel 8 Jun 30 13:59 .s6-svscan
drwxr-xr-x 3 root wheel 5 Jun 30 13:34 .
lrwxr-xr-x 1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x 1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y clockspeed-adjust
/var/service/clockspeed-adjust
Algunas versiones de OS X de stat
pueden carecer de la opción -f%R
para los formatos. En este caso -stat -f%Y
puede ser suficiente. La opción -f%Y
mostrará el objetivo de un enlace simbólico, mientras que -f%R
muestra la ruta de acceso absoluta correspondiente al archivo.
EDITAR:
Si puede usar Perl (Darwin / OS X viene instalado con versiones recientes de perl
), entonces:
perl -MCwd=abs_path -le ''print abs_path readlink(shift);'' linkedfile.txt
trabajará.
readlink -f
hace dos cosas:
- Recorre una secuencia de enlaces simbólicos hasta que encuentra un archivo real.
- Devuelve el nombre canonizado de ese archivo, es decir, su ruta de acceso absoluta.
Si lo desea, simplemente puede crear un script de shell que use el comportamiento de vainilla readlink para lograr lo mismo. Aquí hay un ejemplo. Obviamente, puede insertar esto en su propio script donde le gustaría llamar a readlink -f
#!/bin/sh
TARGET_FILE=$1
cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`
# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
TARGET_FILE=`readlink $TARGET_FILE`
cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`
done
# Compute the canonicalized name by finding the physical path
# for the directory we''re in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT
Tenga en cuenta que esto no incluye ningún manejo de errores. De particular importancia, no detecta ciclos de enlaces simbólicos. Una forma sencilla de hacer esto sería contar la cantidad de veces que recorre el ciclo y fallará si alcanza un número improbable, como 1,000.
EDITADO para usar pwd -P
lugar de $PWD
.
Tenga en cuenta que esta secuencia de comandos espera ser llamada como ./script_name filename
, no -f
, cambie de $1
a $2
si desea poder usar con -f filename
como GNU readlink.
Comenzar actualización
Este es un problema tan frecuente que hemos reunido una biblioteca de Bash 4 para uso libre (licencia MIT) llamada realpath-lib . Está diseñado para emular readlink -f de manera predeterminada e incluye dos suites de prueba para verificar (1) que funciona para un sistema Unix determinado y (2) contra readlink -f si está instalado (pero no es necesario). Además, puede utilizarse para investigar, identificar y desenrollar enlaces simbólicos profundos y rotos, así que puede ser una herramienta útil para diagnosticar problemas de archivos y directorios físicos o simbólicos profundamente anidados. Se puede encontrar en github.com o bitbucket.org .
Actualización final
Otra solución muy compacta y eficiente que no se basa en nada más que Bash es:
function get_realpath() {
[[ ! -f "$1" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp=''pwd -P'' || local pwdp=''pwd'' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
Esto también incluye una configuración de entorno no_symlinks
que proporciona la capacidad de resolver enlaces simbólicos al sistema físico. Siempre que no_symlinks
esté configurado en algo, es decir, no_symlinks=''on''
entonces los enlaces simbólicos se resolverán en el sistema físico. De lo contrario, se aplicarán (la configuración predeterminada).
Esto debería funcionar en cualquier sistema que proporcione Bash y devolverá un código de salida compatible con Bash para fines de prueba.