Comment puis-je obtenir le comportement de GNU readlink-f sur un Mac?

sur Linux, l'utilitaire readlink accepte une option -f qui suit des liens supplémentaires. Cela ne semble pas fonctionner sur Mac et peut-être des systèmes basés BSD. Ce serait l'équivalent?

voici quelques informations de débogage:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
286
demandé sur SamB 2009-06-29 00:26:33

20 réponses

readlink -f fait deux choses:

  1. Il itère le long d'une séquence de liens symboliques jusqu'à ce qu'il trouve un fichier réel.
  2. il renvoie le nom du fichier canonisé , c'est-à-dire son chemin absolu.

si vous le souhaitez, vous pouvez simplement construire un script shell qui utilise le comportement readlink pour réaliser la même chose. Voici un exemple. Évidemment, vous pouvez insérer ceci dans votre propre script où vous voulez appeler readlink -f

#!/bin/sh

TARGET_FILE=

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

notez que cela n'inclut pas le traitement des erreurs. D'une importance particulière, il ne détecte pas les cycles de symlink. Une façon simple de faire cela serait de compter le nombre de fois où vous faites le tour de la boucle et échouez si vous frappez un nombre étonnamment grand, comme 1000.

modifié pour utiliser pwd -P au lieu de $PWD .

notez que ce script s'attend à être appelé comme ./script_name filename , Non -f , changer en si vous voulez pouvoir l'utiliser avec -f filename comme GNU readlink.

139
répondu Keith Smith 2016-08-08 19:19:45

MacPorts et Homebrew fournissent un paquet coreutils contenant greadlink (GNU readlink). Crédit à Michael Kallweitt post in mackb.com.

brew install coreutils

greadlink -f file.txt
342
répondu tomyjwu 2018-03-06 13:47:47

vous pourriez être intéressé par realpath(3) , ou de Python os.path.realpath . Les deux ne sont pas exactement les mêmes; l'appel de bibliothèque C exige que les composants de chemin intermédiaires existent, alors que la version Python n'en a pas.

$ 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

je sais que vous avez dit préférer quelque chose de plus léger qu'un autre langage de script, mais juste au cas où compiler un binaire est insupportable, vous pouvez utiliser Python et des ctypes (disponibles sur Mac OS X 10.5) pour envelopper l'appel de la bibliothèque:

#!/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])

ironiquement, la version C de ce script devrait être plus courte. :)

40
répondu Miles 2016-05-27 10:20:22

je déteste empiler avec encore une autre implémentation, mais j'avais besoin de a) une implémentation portable, pure shell , et b) unité-test de couverture , car le nombre de bordures pour quelque chose comme ça sont non-trivial .

Voir mon projet sur Github pour les tests et le code complet. Ce qui suit est un résumé de la mise en œuvre:

Comme Keith Smith astucieusement fait remarquer, readlink -f fait deux choses: 1) résout symlinks récursivement, et 2) canonicalise le résultat, donc:

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

tout D'abord, la mise en œuvre du résolveur symlink:

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' ""
    fi
}

_prepend_path_if_relative() {
    case "" in
        /* ) printf '%s\n' "" ;;
         * ) printf '%s\n' "/" ;;
    esac 
}

notez qu'il s'agit d'une version légèrement simplifiée de the full implementation . l'implémentation complète ajoute une petite vérification pour les cycles symlink , ainsi que des massages de la sortie un peu.

enfin, la fonction pour canoniser un chemin:

canonicalize_path() {
    if [ -d "" ]; then
        _canonicalize_dir_path ""
    else
        _canonicalize_file_path ""
    fi
}   

_canonicalize_dir_path() {
    (cd "" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "")
    file=$(basename -- "")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

c'est ça, plus ou moins. Assez Simple pour coller dans votre script, mais assez compliqué pour que vous soyez fou de vous fier à n'importe quel code qui n'a pas de tests unitaires pour vos cas d'utilisation.

25
répondu Michael Kropat 2014-04-09 18:34:58
  1. Installer homebrew
  2. Exécuter "brew install coreutils"
  3. Exécuter "greadlink -f chemin"

greadlink est le gnu readlink qui implémente -F. Vous pouvez utiliser macports ou d'autres aussi, je préfère homebrew.

23
répondu Andrew 2012-03-29 02:35:28

une simple doublure en perl qui fonctionne presque partout sans aucune dépendance extérieure:

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

va déréférencer symlinks.

Utilisation dans un script pourrait être comme ceci:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "";}
ABSPATH="$(readlinkf ./non-absolute/file)"
15
répondu JinnKo 2015-10-25 12:20:26

j'ai fait un script appelé realpath personnellement qui ressemble un peu à:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
13
répondu James 2018-02-21 18:37:17

et ça?

function readlink() {
  DIR=$(echo "${1%/*}")
  (cd "$DIR" && echo "$(pwd -P)")
}
9
répondu Peeeech 2012-02-28 14:44:40

FreeBSD et OSX ont une version de stat dérivée de NetBSD.

vous pouvez régler la sortie avec des commutateurs de format (voir les pages de manuel aux liens ci-dessus).

%  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

certaines versions D'OS X de stat peuvent ne pas avoir l'option -f%R pour les formats. Dans ce cas, -stat -f%Y peut suffire. L'option -f%Y affichera la cible d'un lien symbolique, alors que -f%R montre le chemin absolu correspondant au fichier.

EDIT:

si vous pouvez utiliser Perl (Darwin / OS X est installé avec les versions récentes de perl ) puis:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

fonctionnera.

8
répondu G. Cito 2015-10-19 01:56:37

Voici une fonction portable shell qui devrait fonctionner dans tout Bourne comparable shell. Il résoudra la ponctuation relative du chemin ".. ou. "et des liens symboliques déréférentiels.

si, pour une raison quelconque, vous n'avez pas de commande realpath(1), ou readlink(1), ceci peut être alias.

which realpath || alias realpath='real_path'

Profiter:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in 
  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 [ "" = "->" ]
        then FOO=
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

aussi, juste au cas où quelqu'un est intéressé voici comment mettre en œuvre basename et dirname en code shell 100% pur:

## 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##*/}"
}

vous pouvez trouver la version mise à jour de ce code shell sur mon site google: http://sites.google.com/site/jdisnard/realpath

EDIT: Ce code est autorisé sous les termes de la Licence 2-clause (style freeBSD). Une copie de la licence peut être trouvée en suivant l'hyperlien ci-dessus à mon site.

6
répondu masta 2012-02-11 20:10:00

la manière la plus simple de résoudre ce problème et d'activer les fonctionnalités de readlink sur Mac w/ Homebrew installé ou FreeBSD est d'installer le paquet 'coreutils'. Peut également être nécessaire sur certaines distributions Linux et autres OS POSIX.

par exemple, sous FreeBSD 11, j'ai installé en invoquant:

# pkg install coreutils

sur MacOS avec Homebrew, la commande serait:

$ brew install coreutils

Je ne sais pas vraiment pourquoi les autres réponses sont si compliquées, c'est tout. Les fichiers ne sont pas à un autre endroit, ils ne sont pas encore installés.

5
répondu AveryFreeman 2017-01-06 21:28:04

Commencer La Mise À Jour

C'est un problème si fréquent que nous avons mis en place une bibliothèque Bash 4 pour une utilisation libre (licence MIT) appelée realpath-lib . Il est conçu pour imiter par défaut readlink-f et comprend deux suites de test pour vérifier (1) qu'il fonctionne pour un système unix donné et (2) contre readlink-f s'il est installé (mais ce n'est pas nécessaire). Outre, il peut être utilisé pour enquêter, identifier et décompresser profonds, liens symboliques cassés et des références circulaires, de sorte qu'il peut être un outil utile pour diagnostiquer profondément imbriqué problèmes physiques ou symboliques de répertoire et de fichiers. Il peut être trouvé à github.com ou bitbucket.org .

Mise À Jour

une autre solution très compacte et efficace qui ne repose sur rien d'autre:

function get_realpath() {

    [[ ! -f "" ]] && 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

}

Cela inclut également un paramètre d'environnement no_symlinks qui fournit la capacité de résoudre les liens symboliques vers le système physique. Aussi longtemps que no_symlinks est défini à quelque chose, c'est-à-dire no_symlinks='on' alors les liens symboliques seront résolus au système physique. Sinon, ils seront appliqués (le paramètre par défaut).

cela devrait fonctionner sur tout système qui fournit Bash, et retournera un code de sortie compatible Bash à des fins de test.

3
répondu AsymLabs 2014-02-20 11:43:51

Il y a déjà beaucoup de réponses, mais aucune n'a fonctionné pour moi... C'est ce que j'utilise maintenant.

readlink_f() {
  local target=""
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}
3
répondu estani 2017-04-06 09:43:15

mieux vaut tard que jamais, je suppose. J'étais motivé pour développer ceci spécifiquement parce que mes scripts Fedora ne fonctionnaient pas sur le Mac. Le problème est les dépendances et Bash. Les Macs n'en ont pas, ou s'ils en ont, ils sont souvent ailleurs (un autre chemin). La manipulation de chemins de dépendances dans un script de Bash multiplateformes est au mieux un casse - tête et au pire un risque de sécurité-il est donc préférable d'éviter leur utilisation, si possible.

la fonction get_realpath() ci-dessous est simple, Bash-centric, et pas de dépendances sont nécessaires. Je n'utilise que les builtins Bash echo et cd . Il est également assez sûr, car tout est testé à chaque étape du chemin et il renvoie les conditions d'erreur.

si vous ne voulez pas suivre les liens symboliques, alors mettez set-P au début du script, mais autrement cd devrait résoudre les liens symboliques par défaut. Il a été testé avec des arguments de fichier qui sont {absolute | relative | symlink / local} et il renvoie le chemin absolu au fichier. Jusqu'à présent, nous n'avons eu aucun problème avec elle.

function get_realpath() {

if [[ -f "" ]]
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

}

vous pouvez combiner cela avec d'autres fonctions get_dirname, get_filename, get_stemname et validate_path. Ceux-ci peuvent être trouvés à notre dépôt GitHub en tant que realpath - lib (pleine divulgation-c'est notre produit, mais nous l'offrons gratuitement à la communauté sans aucune restriction). Il pourrait également servir comme un outil pédagogique - c'est bien documentée.

nous avons fait de notre mieux pour appliquer les pratiques dites "de Bash moderne", mais Bash est un grand sujet et je suis certain qu'il y aura toujours place à l'amélioration. Il nécessite Bash 4+ mais pourrait être fait pour fonctionner avec des versions plus anciennes si elles sont toujours présentes.

1
répondu AsymLabs 2013-10-03 15:54:30

Vraiment de la plate-forme indpendent serait également cette R-onliner

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

pour réellement imiter readlink -f <path> , $2 au lieu de $1 devrait être utilisé.

1
répondu Holger Brandl 2015-12-10 20:17:01

j'ai écrit un utilitaire realpath pour OS X qui peut fournir les mêmes résultats que readlink -f .



voici un exemple:

(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 vous utilisez MacPorts, vous pouvez l'installer avec la commande suivante: sudo port selfupdate && sudo port install realpath .

0
répondu user454322 2015-08-25 11:31:59

C'est ce que j'utilise:

stat -f %N $your_path

-1
répondu Carlos Nunez 2015-12-23 19:23:45

les chemins de readlink sont différents entre mon système et le vôtre. Veuillez essayer de spécifier le chemin complet:

/sw/sbin/readlink -f

-2
répondu ennuikiller 2009-07-12 00:43:17

Perl a une fonction readlink (par exemple comment copier des liens symboliques en Perl? ). Cela fonctionne sur la plupart des plateformes, y compris OS X:

perl -e "print readlink '/path/to/link'"

par exemple:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c
-2
répondu jonjlee 2017-05-23 12:18:18

la réponse de @Keith Smith donne une boucle infinie.

voici ma réponse, que je n'utilise que sur SunOS (SunOS manque tellement de commandes POSIX et GNU).

c'est un fichier de script que vous devez mettre dans l'un de vos répertoires $PATH:

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link=""
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"
-2
répondu scavenger 2014-09-24 15:53:48