¿Cómo puedo obtener el comportamiento de readlink -f de GNU en una Mac?


377

En Linux, la readlinkutilidad acepta una opción -fque sigue enlaces adicionales. Esto no parece funcionar en Mac y posiblemente en sistemas basados ​​en BSD. ¿Cuál sería el equivalente?

Aquí hay información de depuración:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

1
Un poco tarde, pero su pregunta no menciona el shell que usa. Esto es relevante, porque readlinkpuede ser un comando incorporado o un comando externo.
0xC0000022L

1
Eso explicaría la diferencia quizás. Sin embargo, estoy bastante seguro de que usé bash en ambas ocasiones.
troelskn

1
¿Por qué esta opción es ilegal en Mac?
CommaToast

3
Realmente deseo que Apple haga que OS X admita más rutas de Linux predeterminadas y aborde cosas como esta. Podrían hacerlo sin romper nada, ¿no?
CommaToast

1
@CommaToast Bueno, se envían con Perl, así que ++ :-) ... abra Terminal.app y escriba: touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile... en mi caso, veo: / Users / cito / myfile`. Lo agregué a mi respuesta a continuación. Salud.
G. Cito

Respuestas:


181

readlink -f hace dos cosas:

  1. Se itera a lo largo de una secuencia de enlaces simbólicos hasta que encuentra un archivo real.
  2. Devuelve el nombre canonicalizado de ese archivo, es decir, su nombre de ruta absoluto.

Si lo desea, puede crear un script de shell que utilice el comportamiento de enlace de lectura de vainilla para lograr lo mismo. Aquí hay un ejemplo. Obviamente, podría insertar esto en su propio script donde le gustaría llamarreadlink -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 enlace simbólico. Una manera simple de hacer esto sería contar la cantidad de veces que da la vuelta al ciclo y falla si golpea un número improbablemente grande, como 1,000.

EDITADO para usar en pwd -Plugar de $PWD.

Tenga en cuenta que este script espera que se le llame como ./script_name filename, no -f, cambie $1a $2si desea poder usarlo con el -f filenameenlace de lectura GNU similar.


1
Por lo que puedo decir, eso no funcionará si un directorio principal de la ruta es un enlace simbólico. P.ej. si foo -> /var/cux, entonces foo/barno se resolverá, porque barno es un enlace, aunque lo fooes.
troelskn

1
Ah Si. No es tan simple, pero puede actualizar el script anterior para lidiar con eso. Voy a editar (reescribir, realmente) la respuesta en consecuencia.
Keith Smith el

Bueno, un enlace podría estar en cualquier parte del camino. Supongo que el script podría iterar sobre cada parte del camino, pero se vuelve un poco complicado entonces.
troelskn el

1
Gracias. El problema es que $ PWD nos está dando el directorio de trabajo lógico, basado en los valores en los enlaces simbólicos que hemos seguido. Podemos obtener el directorio físico real con 'pwd -P'. Debe computarlo persiguiendo ".." hasta la raíz del sistema de archivos. Actualizaré el script en mi respuesta en consecuencia.
Keith Smith el

12
Gran solución, pero no trata adecuadamente con rutas que necesitan escapar , como nombres de archivos o directorios con espacios incrustados . Para remediar eso, use cd "$(dirname "$TARGET_FILE")" y TARGET_FILE=$(readlink "$TARGET_FILE")y TARGET_FILE=$(basename "$TARGET_FILE")en los lugares apropiados en el código anterior.
mklement0

439

MacPorts y Homebrew proporcionan un paquete coreutils que contiene greadlink(GNU readlink). Crédito a la publicación de Michael Kallweitt en mackb.com.

brew install coreutils

greadlink -f file.txt

Si está llamando a readlink desde lugares que no sean bash, otra solución sería eliminar / usr / bin / readlink y luego crear un enlace a / usr / local / bin / greadlink.
chinglun

8
Por favor no, a menos que a) escriba software para su propio b) quiera meterse con otros. Escríbalo de tal manera que "simplemente funcione" para cualquiera.
kgadek

14
@ching Haga esto en su lugar .bashrc:export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine

1
La pregunta pide el equivalente en un sistema OS X o BSD, esto no responde eso. También deberás instalar Homebrew. También instalará muchas cosas aparte de GNU Readlink. Si está escribiendo una secuencia de comandos para instalaciones estándar de OS X, esta respuesta no ayudará. Puede usar una combinación de pwd -Py readlinken OS X, sin instalar nada
Jason S

44
Quería el GNU y otras utilidades en mi Mac que creo que funcionan bien, sin el prefijo, y de fácil acceso desde mi usuario PATH. Hizo lo anterior comenzando con brew install <package> --default-names. Muchas veces una pregunta en este sitio se ha respondido ligeramente, o más, fuera de los requisitos técnicos del autor de la pregunta, pero dentro de una situación similar, y aún ha sido muy útil. topbug.net/blog/2013/04/14/…
Pysis

43

Te puede interesar realpath(3), o Python os.path.realpath. Los dos no son exactamente lo mismo; la llamada a la biblioteca C requiere que existan componentes de ruta intermedios, mientras que la versión 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

Sé que dijiste que preferirías algo más liviano que otro lenguaje de script, pero en caso de que compilar un binario sea insufrible, puedes usar Python y ctypes (disponibles en Mac OS X 10.5) para ajustar la llamada a 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. :)


Sí, de realpathhecho es lo que quiero. Pero parece bastante incómodo que tenga que compilar un binario para obtener esta función de un script de shell.
troelskn

44
¿Por qué no utilizar la línea única de Python en el script de shell? (No es tan diferente de una llamada de una sola línea readlink, ¿verdad?)
Telemachus

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"funciona con caminos como'~/.symlink'
Wes Turner

@troelskin ¡No me di cuenta de que Perl, Python, etc. estaban "permitidos"! ... en ese caso voy a agregar perl -MCwd=abs_path -le 'print abs_path readlink(shift);'a mi respuesta :-)
G. Cito

27

Odio seguir con otra implementación, pero necesitaba a) una implementación portátil y de shell puro , yb) cobertura de prueba unitaria , ya que el número de casos límite para algo como esto no es trivial .

Vea 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 -fhace dos cosas: 1) resuelve los enlaces simbólicos de forma recursiva y 2) canoniza el resultado, por lo tanto:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

Primero, la implementación del solucionador de enlaces simbólicos:

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 verificación para los ciclos de enlace simbólico , así como también masajea un poco la salida.

Finalmente, la función para canonizar una ruta:

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 todo, más o menos. Suficientemente simple para pegar en su script, pero lo suficientemente complicado como para que se vuelva loco al confiar en cualquier código que no tenga pruebas unitarias para sus casos de uso.


3
Esto no es POSIX - utiliza la localpalabra clave que no es POSIX
go2null

Esto no es equivalente a readlink -f, ni de realpath. Asumiendo una Mac con la fórmula coreutils instalada (con la greadlinkdisponible), intente esto con ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..imprime su directorio de inicio (resolvió el /tmp/linkeddirenlace antes de aplicar la ..referencia de directorio principal), pero su código produce /private/tmp/como /tmp/linkeddir/..no es un enlace simbólico, pero -d /tmp/linkeddir/..es verdadero y lo cd /tmp/linkeddir/..lleva a /tmp, cuya ruta canónica es /private/tmp. Su código hace lo que realpath -Lhace, en su lugar.
Martijn Pieters

27

Un simple one-liner en perl que seguramente funcionará en casi todas partes sin ninguna dependencia externa:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

Desreferenciará enlaces simbólicos.

El uso en un script podría ser así:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

44
para obtener una nueva línea final, agregue -l, comoperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar

26
  1. Instalar homebrew
  2. Ejecute "brew install coreutils"
  3. Ejecute "greadlink -f path"

greadlink es el gnu readlink que implementa -f. Puedes usar macports u otros también, prefiero homebrew.


2
No puedo ver ninguna diferencia en la respuesta dada en 2010 stackoverflow.com/a/4031502/695671
Jason S

Si necesita usar estos comandos con sus nombres normales, puede agregar un directorio "gnubin" a su PATH desde su bashrc como: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov

20

Hice un script llamado realpath personalmente que se parece a algo así como:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

Debería cambiar eso a sys.argv[1]si desea que el script imprima la ruta real del primer argumento. sys.argv[0]solo imprime la ruta real del script pyton en sí mismo, lo que no es muy útil.
Jakob Egger

17
Aquí está la versión de alias, para ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

Gran respuesta, y una gran frase @jwhitlock. Desde el OP citado readlink -f, aquí hay una versión modificada del alias de @jwhitlock para admitir una posible -fbandera: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codesniffer

11

¿Qué hay de esto?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

Esta es la única solución que funciona para mí; Necesito analizar caminos como ../../../->/
keflavich

Esto no puede funcionar si tiene un enlace simbólico, /usr/bin/por ejemplo, como alternativesle gusta tener al sistema.
akostadinov

No funciona para archivos individuales, solo directorios. GNU readlink funciona con directorios, archivos, enlaces simbólicos, etc.
codeniffer

7

Aquí hay una función de shell portátil que debería funcionar en CUALQUIERA shell comparable de Bourne. Resolverá la puntuación relativa de ruta ".. o". y desreferenciar enlaces simbólicos.

Si por alguna razón no tiene un comando realpath (1) o readlink (1), esto puede ser 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"
}

Además, en caso de que alguien esté interesado, 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 tiene licencia 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.


Real_path devuelve / filename en lugar de / path / to / filename en Mac OS X Lion en el shell bash.

@Barry Lo mismo sucede en OSX Snow Leopard. Peor aún, las llamadas sucesivas a real_path () concatenan la salida.
Dominique

@Dominique: no es sorprendente, ya que el interior de la función no se ejecuta en su propio subshell ... feo y frágil de hecho.
0xC0000022L

7

FreeBSD y OSX tienen una versión destat derivada de NetBSD.

Puede ajustar la salida con modificadores de formato (consulte las páginas del 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 stat pueden carecer de la -f%Ropción de formatos. En este caso -stat -f%Ypuede ser suficiente. La -f%Yopción mostrará el objetivo de un enlace simbólico, mientras que -f%Rmuestra el nombre de ruta absoluto 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á.


2
Esto es cierto en FreeBSD 10, pero no en OS X 10.9.
Zanchey

Buena atrapada. La página del manual de estadísticas citada es para OS / X 10.9. La Ropción de -f%falta. Posiblemente stat -f%Yda la salida deseada. Ajustaré la respuesta. Tenga en cuenta que la statherramienta apareció en FreeBSD en la versión 4.10 y NetBSD en la versión 1.6.
G. Cito

El comando Perl no funciona correctamente en 10.15. Déle un archivo en el directorio actual y le dará el directorio actual (sin el archivo).
codeniffer

7

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. Los archivos no están en un lugar diferente, simplemente no están instalados todavía.


77
brew install coreutils --with-default-namespara ser más especifico. O que va a terminar con "greadlink" en lugar ...
Henk

No puedo ver ninguna diferencia en las respuestas de 2010 stackoverflow.com/a/4031502/695671 y 2012 stackoverflow.com/a/9918368/695671 La pregunta es en realidad "... en Mac y posiblemente en sistemas basados ​​en BSD. ¿Qué sería el equivalente sea? " No creo que esta sea una muy buena respuesta a esa pregunta. Muchas razones, pero puede estar apuntando a máquinas Mac OS sin la capacidad de instalar nada. pwd -Pes bastante simple pero desaparece entre muchas otras respuestas, incluidas las respuestas repetidas, de ahí el voto negativo.
Jason S

3

Comenzar actualización

Este es un problema tan frecuente que hemos creado una biblioteca Bash 4 para uso gratuito (licencia MIT) llamada realpath-lib . Está diseñado para emular readlink -f de forma predeterminada e incluye dos conjuntos de pruebas para verificar (1) que funciona para un sistema Unix determinado y (2) contra readlink -f si está instalado (pero esto no es obligatorio). Además, se puede usar para investigar, identificar y desenrollar enlaces simbólicos profundos y rotos y referencias circulares, por lo que puede ser una herramienta útil para diagnosticar problemas de directorios y archivos físicos o simbólicos profundamente anidados. Se puede encontrar en github.com o bitbucket.org .

Fin de actualización

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_symlinksque proporciona la capacidad de resolver enlaces simbólicos al sistema físico. Siempre y cuando no_symlinksse establezca en algo, es decir no_symlinks='on', 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.


3

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"
}

1
Esto no funciona si $targetes una ruta, solo funciona si es un archivo.
MaxGhost

3

Una manera perezosa que funciona para mí

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

Mejor tarde que nunca, supongo. Estaba motivado para desarrollar esto específicamente porque mis scripts de Fedora no funcionaban 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 un dolor de cabeza en el mejor de los casos y un riesgo de seguridad en el peor, 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. Solo uso el eco integrado de Bash y CD de . 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 en la parte delantera del script, pero de lo contrario, cd debería resolver los enlaces simbólicos por defecto. 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 eso.

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 gratuitamente a la comunidad sin restricciones). También podría servir como herramienta de instrucción: está bien documentado.

Hemos hecho todo lo posible para aplicar las llamadas prácticas de 'Bash moderno', pero Bash es un tema importante y estoy seguro de que siempre habrá margen de mejora. Requiere Bash 4+, pero podría funcionar con versiones anteriores si todavía están disponibles.


1

Dado que mi trabajo es utilizado por personas con Linux no BSD y macOS, he optado por usar estos alias en nuestros scripts de compilación (sed incluido porque 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
}

Comprobación opcional que puede agregar para instalar automáticamente homebrew + coreutils dependencias de :

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" necesita verificar a otros ... pero eso probablemente se acerca a la marca 80/20.


1

Explicación

coreutils es un brew paquete que instala las utilidades principales de GNU / Linux que corresponden a la implementación de Mac OSX de ellas para que pueda usarlas

Puede encontrar programas o utilidades en su sistema mac osx que parecen similares a los coreutils de Linux ("Core Utilities") pero difieren en algunos aspectos (como tener diferentes indicadores).

Esto se debe a que la implementación de Mac OSX de estas herramientas es diferente. Para obtener el comportamiento original de GNU / Linux, puede instalar el coreutilspaquete a través del brewsistema de administración de paquetes.

Esto instalará las utilidades principales correspondientes, precedidas por g. Por ejemplo readlink, encontrará un greadlinkprograma correspondiente .

Para que readlinkfuncione como la implementación GNU readlink( greadlink), puede crear un alias simple después de instalar coreutils.

Implementación

  1. Instalar cerveza

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

  1. Instale el paquete coreutils

brew install coreutils

  1. Crea un alias

Puede colocar su alias en ~ / .bashrc, ~ / .bash_profile, o donde esté acostumbrado a mantener sus alias bash. Yo personalmente mantengo el mío en ~ / .bashrc

alias readlink=greadlink

Puede crear alias similares para otros coreutils como gmv, gdu, gdf, etc. Pero tenga en cuenta que el comportamiento de GNU en una máquina Mac puede ser confuso para otros acostumbrados a trabajar con coreutils nativos, o puede comportarse de manera inesperada en su sistema Mac.


0

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á usando MacPorts, puede instalarlo con el siguiente comando: sudo port selfupdate && sudo port install realpath.


0

Verdaderamente independiente de la plataforma también sería este R-onliner

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

Para imitar realmente readlink -f <path>, sería necesario usar $ 2 en lugar de $ 1.


Excepto que R no se encuentra en ningún sistema por defecto. Todavía no en Mac (a partir del 10.15), de eso se trata esta pregunta.
codeniffer

0

readlink -fImplementación compatible con POSIX para scripts de shell POSIX

https://github.com/ko1nksm/readlinkf

Esto es compatible con POSIX (sin bashism). No usa ni readlinkni realpath. He verificado que es exactamente lo mismo comparándolo con GNU readlink -f(ver resultados de la prueba ). Tiene manejo de errores y buen rendimiento. Puede reemplazar con seguridad desde readlink -f. La licencia es CC0, por lo que puede usarla para cualquier proyecto.

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  target=$1 max_symlinks=10 CDPATH=''

  [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
  [ -d "${target:-/}" ] && target="$target/"

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -gt 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      cd -P "${target%/*}/" 2>/dev/null || break
      target=${target##*/}
    fi

    if [ ! -L "$target" ]; then
      target="${PWD%/}${target:+/}$target"
      printf '%s\n' "${target:-/}"
      return 0
    fi

    # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
    #   <file mode>, <number of links>, <owner name>, <group name>,
    #   <size>, <date and time>, <pathname of link>, <contents of link>
    # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
    link=$(ls -dl "$target" 2>/dev/null) || break
    target=${link#*" $target -> "}
  done
  return 1
}

Por favor, consulte el último código. Puede algunos arreglado.


-2

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

55
Esto hace lo mismo que readlinksin la -fopción (sin recursión, sin ruta absoluta), por lo que también podría usarlo readlinkdirectamente.
mklement0

-2

La respuesta de @Keith Smith da un bucle infinito.

Aquí está mi respuesta, que solo uso en SunOS (SunOS pierde muchos comandos POSIX y GNU).

Es un archivo de script que debe colocar en uno de sus directorios $ 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"


-5

Las rutas a readlink son diferentes entre mi sistema y el tuyo. Intente especificar la ruta completa:

/sw/sbin/readlink -f


1
¿Y cuál es exactamente la diferencia entre / sw / sbin y / usr / bin? ¿Y por qué difieren los dos binarios?
troelskn

44
Ajá ... así que Fink contiene un reemplazo readlinkque es compatible con GNU. Es bueno saberlo, pero no resuelve mi problema, ya que necesito que mi script se ejecute en la máquina de otras personas, y no puedo requerir que instalen fink para eso.
troelskn el

No estás respondiendo la pregunta original. Por favor reescribe tu respuesta.
Jaime Hablutzel el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.