¿Cómo puedo obtener el comportamiento de GNU readlink -f en un Mac?
Pregunta
En Linux, el readlink
utilidad acepta una opción -f
que sigue enlaces adicionales.Esto no parece funcionar en Mac y, posiblemente, sistemas basados en BSD.Lo que sería el equivalente a ser?
Aquí un poco de información de depuración:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
Solución
readlink -f
hace dos cosas:
- Se repite a lo largo de una secuencia de enlaces simbólicos hasta que encuentra un archivo real.
- Devuelve ese archivo canonizadas nombre—es decir, su ruta de acceso absoluta.
Si desea, puede crear un script de shell que utiliza vainilla readlink comportamiento para lograr la misma cosa.He aquí un ejemplo.Obviamente se puede poner esto en su propia secuencia de comandos en los que desea llamar 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 el tratamiento de errores.De particular importancia, no detecta el enlace simbólico ciclos.Una manera sencilla de hacerlo sería la de contar el número de veces que usted ve alrededor del bucle y fallar si usted golpea una gran cantidad, tal como 1.000.
EDITADO el uso de pwd -P
en lugar de $PWD
.
Tenga en cuenta que este script espera ser llamado como ./script_name filename
, no -f
, cambiar $1
a $2
si usted quiere ser capaz de utilizar con -f filename
como GNU readlink.
Otros consejos
MacPorts y Homebrew proporcionar un coreutils paquete que contiene greadlink
(GNU readlink).Crédito a Michael Kallweitt post en mackb.com.
brew install coreutils
greadlink -f file.txt
Usted puede estar interesado en realpath(3)
, o Python os.path.realpath
.Los dos no son exactamente las mismas;la biblioteca de C de llamada requiere que el intermediario componentes de la ruta que existen, mientras que la versión de Python no.
$ 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
Yo sé que usted dijo que usted prefiere algo más ligero que otro lenguaje de scripting, pero en caso de que la compilación de un binario es insufrible, usted puede usar Python y ctypes (disponible en Mac OS X 10.5) para envolver 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 debe ser más corto.:)
Yo odio a la pila con otra aplicación, pero necesitaba una) un portátil, pura cáscara de aplicación, y b) pruebas unitarias de cobertura de, como el número de los bordes de los casos para algo como esto se no triviales.
Ver mi proyecto en Github para las pruebas y el código completo.Lo que sigue es un resumen de la aplicación:
Como Keith Smith astutamente señala, readlink -f
hace dos cosas:1) resuelve los enlaces simbólicos de forma recursiva, y 2) canonicalizes el resultado, por lo tanto:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
En primer lugar, el enlace simbólico de resolución de implementación:
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 un poco la versión simplificada de la plena aplicación. La plena aplicación añade una pequeña comprobar enlace simbólico ciclos, así como los masajes, la salida de un bit.
Por último, la función de canonicalizing una ruta de acceso:
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 pegar en tu script, pero bastante complicado que sería una locura confiar en cualquier código que no tiene unidad de pruebas para sus casos de uso.
- Instalar homebrew
- Ejecutar "brew install coreutils"
- Ejecutar "greadlink f-path"
greadlink es la gnu readlink que implementa -f.Usted puede utilizar macports o de otras personas así, yo prefiero homebrew.
Una simple línea en perl que seguro que funcionan en casi todas partes sin dependencias externas:
perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file
Va a eliminar los enlaces simbólicos.
El uso de un script podría ser como esta:
readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"
He hecho un script llamado realpath personalmente la que se ve un poco algo como:
#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
Lo que acerca de esto?
function readlink() {
DIR="${1%/*}"
(cd "$DIR" && echo "$(pwd -P)")
}
FreeBSD y OSX tiene una versión de stat
derivado de NetBSD.
Se puede ajustar la salida con formato de interruptores (vea las páginas de manual en los enlaces de arriba).
% 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 stat
pueden carecer de la -f%R
opción para los formatos.En este caso -stat -f%Y
puede ser suficiente.El -f%Y
opción de mostrar el destino de un enlace simbólico, mientras que -f%R
muestra la ruta de acceso absoluta correspondiente al archivo.
EDITAR:
Si usted es capaz de utilizar Perl (Darwin/OS X viene instalado con las recientes versiones de perl
), entonces:
perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt
va a trabajar.
Aquí es un portátil de shell de la función que deben trabajar en CUALQUIER Bourne comparable shell.Va a resolver la ruta de acceso relativa de puntuación "..o ." y eliminar los enlaces simbólicos.
Si por alguna razón usted no tiene un realpath(1) de mando, o readlink(1) este puede ser un alias.
which realpath || alias realpath='real_path'
Disfrutar de:
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"
}
además, sólo en caso de que alguien se interesa aquí es cómo implementar basename y dirname en 100% puro de código de shell:
## 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##*/}"
}
Usted 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 está licenciado bajo los términos de la 2-cláusula (freeBSD estilo) de licencia.Una copia de la licencia pueden encontrarse siguiendo el anterior hipervínculo a mi sitio.
La forma más sencilla de resolver este problema y permitir la funcionalidad de readlink en Mac w/ Homebrew instalado o FreeBSD es instalar 'coreutils' paquete.También puede ser necesario en ciertas distribuciones de Linux y de otros POSIX OS.
Por ejemplo, en FreeBSD 11, he instalado por la invocación:
# pkg install coreutils
En MacOS con Homebrew, el comando sería:
$ brew install coreutils
No muy segura de por qué las otras respuestas son tan complicados, que es todo allí está a él.Los archivos no están en un lugar diferente, que no se ha instalado todavía.
Comenzar La Actualización
Este es un problema frecuente que nos han puesto juntos un Bash 4 de la biblioteca para el uso libre (Licencia MIT) llamado realpath-lib.Esto está diseñado para emular readlink -f por defecto, e incluye dos conjuntos de pruebas para comprobar (1) que trabaja para un determinado sistema unix y (2) en contra de readlink -f si está instalado (pero esto no es necesario).Además, puede ser utilizado para investigar, identificar y relajarse profundamente, roto enlaces y referencias circulares, por lo que puede ser una herramienta útil para el diagnóstico de la anidadas física o simbólica de directorio y de archivo de los problemas.Se puede encontrar en github.com o bitbucket.org.
Actualización De Fin De
Otra muy compacto y eficiente solución que no dependa de nada, pero 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 para resolver los enlaces simbólicos en el sistema físico.Mientras no_symlinks
se establece para algo, es decir, no_symlinks='on'
a continuación, los enlaces simbólicos se resolverán en el sistema físico.De lo contrario se aplica (la configuración predeterminada).
Esto debería funcionar en cualquier sistema que proporciona Bash, y devolverá un Bash compatible con el código de salida para propósitos de prueba.
Ya hay un montón de 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"
}
Mejor tarde que nunca, supongo.Me motivó a desarrollar este en concreto porque mi Fedora secuencias de comandos no funcionan en Mac.El problema es que las dependencias y Bash.Los Macs no los tienen, o si lo hacen, a menudo están en algún otro lugar (otro camino).La dependencia de la ruta de la manipulación en una cruz-plataforma de script de Bash es un dolor de cabeza en el mejor y un riesgo para la seguridad en el peor, así que es mejor evitar su uso, si es posible.
La función get_realpath() a continuación es simple, Bash-céntrica, y dependencias no son necesarios.Yo sólo utiliza el Bash builtins echo y cd.También es bastante seguro, como todo lo que se pone a prueba en cada etapa del camino y devuelve condiciones de error.
Si usted no quiere seguir enlaces simbólicos, a continuación, poner set-P en la parte delantera de la secuencia de comandos, pero de otra manera cd debe resolver los enlaces simbólicos de forma predeterminada.Se ha probado con argumentos de archivo que son {absoluta | relativa | symlink | local} y devuelve la ruta absoluta al archivo.Hasta ahora no hemos tenido ningún problema con él.
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
}
Esto se puede combinar con otras funciones get_dirname, get_filename, get_stemname y validate_path.Estos se pueden encontrar en nuestro repositorio de GitHub como realpath-lib (revelación completa - este es nuestro producto, sino que ofrecen a la comunidad sin ningún tipo de restricciones).También podría servir como una herramienta de enseñanza - está bien documentado.
Hemos hecho todo lo posible para aplicar el llamado 'moderno Bash' prácticas, pero Bash es un gran tema y estoy seguro de que siempre habrá espacio para mejorar.Se requiere Bash 4+, pero podría ser hecho para trabajar con las versiones anteriores si todavía están alrededor.
Verdaderamente la plataforma indpendent sería también este R-onliner
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
Realmente imitar readlink -f <path>
, $2 en lugar de $1 tendría que ser utilizado.
Ya que mi trabajo es utilizado por gente con no-BSD, Linux así como macOS, he optado por el uso de estos alias en nuestros scripts de generación (sed
incluido, ya que tiene problemas similares):
##
# If you're running macOS, use homebrew to install greadlink/gsed first:
# brew install coreutils
#
# Example use:
# # Gets the directory of the currently running script
# dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
# alias al='pico ${dotfilesDir}/aliases.local'
##
function globalReadlink () {
# Use greadlink if on macOS; otherwise use normal readlink
if [[ $OSTYPE == darwin* ]]; then
greadlink "$@"
else
readlink "$@"
fi
}
function globalSed () {
# Use gsed if on macOS; otherwise use normal sed
if [[ $OSTYPE == darwin* ]]; then
gsed "$@"
else
sed "$@"
fi
}
Opcional de verificación se podría añadir a instalar automáticamente homebrew + coreutils dependencias:
if [[ "$OSTYPE" == "darwin"* ]]; then
# Install brew if needed
if [ -z "$(which brew)" ]; then
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)";
fi
# Check for coreutils
if [ -z "$(brew ls coreutils)" ]; then
brew install coreutils
fi
fi
Supongo que para ser verdaderamente "global" es necesario que busque a los demás...pero que viene probablemente cerca del 80/20 marca.
Escribí un realpath utilidad para OS X que puede ofrecer los mismos resultados que readlink -f
.
He aquí 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 usted está usando MacPorts, puedes instalarlo con el siguiente comando: sudo port selfupdate && sudo port install realpath
.
Explicación
coreutils es un brew
paquete que instala GNU/Linux básico de las utilidades que corresponden a la Mac OSX aplicación de ellos de modo que usted puede utilizar los
Usted puede encontrar programas o utilties en su sistema mac osx que parecen similares a Linux coreutils ("Core Utilities"), pero que difieren en algunos aspectos (tales como tener diferentes banderas).
Esto es debido a que el Mac OSX implementación de estas herramientas son diferentes.Para obtener el original de GNU/Linux como el comportamiento puede instalar el coreutils
paquete a través de la brew
sistema de gestión de paquetes.
Esto instalará el correspondiente núcleo de utilidades, con el prefijo g
.E. g.para readlink
, encontrará correspondiente greadlink
programa.
Con el fin de hacer readlink
realizar como el GNU readlink
(greadlink
de la implementación, se puede hacer un simple alias después de instalar coreutils.
La aplicación
- Instalar brew
Siga las instrucciones que aparecen en https://brew.sh/
- Instalar el paquete coreutils
brew install coreutils
- Crear un Alias
Usted puede colocar su alias en ~/.bashrc, ~/.bash_profile, o dondequiera que se utilizan para mantener su alias de bash.Yo personalmente mantener mina en ~/.bashrc
alias readlink=greadlink
Usted puede crear similar alias para otros coreutils, tales como gmv, gdu, gdf, y así sucesivamente.Pero ten cuidado de que la GNU comportamiento en una máquina mac puede ser confuso para los otros que se utiliza para trabajar con los nativos de coreutils, o que se comporte de manera inesperada en su sistema mac.
Esto es lo que yo uso:
stat -f %N $your_path
Las rutas de acceso a readlink son diferentes entre mi sistema y la de los tuyos.Por favor, intente especificar la ruta completa:
/sw/sbin/readlink -f
Perl tiene una función readlink (por ejemplo, ¿Cómo puedo copiar los enlaces simbólicos en Perl?).Esto funciona en la mayoría de las plataformas, incluyendo 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
La respuesta de @Keith Smith da un bucle infinito.
Aquí está mi respuesta, la que yo uso sólo en SunOS (SunOS echo tanto de menos POSIX y comandos de GNU).
Es un archivo de secuencia de comandos que tienes que poner en uno de sus $RUTA de directorios:
#!/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"