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 ...]
¿Fue útil?

Solución

readlink -f hace dos cosas:

  1. Se repite a lo largo de una secuencia de enlaces simbólicos hasta que encuentra un archivo real.
  2. 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.

  1. Instalar homebrew
  2. Ejecutar "brew install coreutils"
  3. 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 statderivado 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

  1. Instalar brew

Siga las instrucciones que aparecen en https://brew.sh/

  1. Instalar el paquete coreutils

brew install coreutils

  1. 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"
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top