Frage

Auf Linux -, die readlink utility akzeptiert eine option -f folgt weitere links.Dies scheint nicht zu funktionieren auf Mac und möglicherweise BSD-basierte Systeme.Was wäre das äquivalent sein?

Hier sind einige debug-Informationen:

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

Lösung

readlink -f macht zwei Dinge:

  1. Es iteriert entlang einer Folge von Symlinks, bis es eine tatsächliche Datei findet.
  2. Sie gibt die Datei canonicalized name-d.h. Sein absoluter Pfadnamen.

Wenn Sie möchten, können Sie einfach ein Shell-Skript erstellen, die Vanille-Readlink Verhalten verwendet, das Gleiche zu erreichen. Hier ist ein Beispiel. Offensichtlich können Sie dies in Ihrem eigenen Skript einfügen, wo Sie möchten, dass readlink -f anrufen

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

Beachten Sie, dass dies beinhaltet keine Fehlerbehandlung. Von besonderer Bedeutung, erkennt es nicht Symlink-Zyklen. Ein einfacher Weg, dies zu tun wäre, um die Anzahl der Sie rund um die Schleife gehen zu zählen und scheitern, wenn Sie eine unglaublich große Zahl getroffen, wie 1000.

EDITED pwd -P statt $PWD zu verwenden.

Beachten Sie, dass dieses Skript wie ./script_name filename, ohne -f, ändern $1 $2 aufgerufen werden erwartet, wenn Sie mit -f filename wie GNU Readlink zu nutzen, um der Lage sein soll.

Andere Tipps

MacPorts und Homebrew bieten einen coreutils Paket enthält greadlink (GNU Readlink). Wir danken Michael Kallweitt Post in mackb.com.

brew install coreutils

greadlink -f file.txt

Sie können daran interessiert sein realpath(3) oder Python os.path.realpath . Die beiden sind nicht genau das gleiche; die C-Bibliothek Anruf erfordert, dass Vermittler Pfadkomponenten vorhanden sind, während die Python-Version nicht.

$ 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

Ich weiß, Sie sagten, Sie etwas leichter als eine andere Skriptsprache bevorzugen würden, aber nur für den Fall einer binären Kompilierung unausstehlich ist, können Sie Python und ctypes (auf Mac OS X 10.5) verwenden, um die Bibliotheksaufruf zu wickeln:

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

Ironischerweise sollte die C-Version dieses Skript kürzer. :)

Ich hasse noch eine weitere Implementierung anhäufen, aber ich brauchte a) eine tragbare, reine Shell Implementierung , und b) Einheit-Testabdeckung , da die Zahl kanten Fälle für so etwas sind nicht-trivial .

Siehe mein Projekt auf Github für Tests und vollständigen Code. Was folgt, ist eine Übersicht über die Implementierung:

Wie Keith Smith astutely weist darauf hin, readlink -f macht zwei Dinge: 1) löst Symlinks rekursiv, und 2) Kanonisiert das Ergebnis, also:

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

Zuerst wird die Symlink Resolver Umsetzung:

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 
}

Beachten Sie, dass es sich um eine leicht vereinfachte Version von . Die vollständige Umsetzung fügt einen kleinen Scheck für Symlink Zyklen , sowie Massagen der Ausgang ein bisschen.

Schließlich ist die Funktion für Kanonisierung einen Pfad:

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

Das ist es, mehr oder weniger. Einfach genug in das Skript einzufügen, aber schwierig genug, dass man verrückt sein würde auf jeden Code zu verlassen, die nicht Unit-Tests für Ihre Anwendungsfälle haben.

  1. Install Homebrew
  2. Ausführen "Gebräu installieren coreutils"
  3. Ausführen "greadlink -f Pfad"

greadlink ist das Gnu Readlink, die -f implementiert. Sie können Macports oder andere auch verwenden, ziehe ich Homebrew.

Ein einfacher Einzeiler in Perl, die fast überall arbeiten ohne externe Abhängigkeiten sicher ist:

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

Will dereferenzieren Symlinks.

Verwendung in einem Skript wie folgt sein könnte:

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

Ich habe ein Skript realpath persönlich genannt, die wie ein wenig etwas aussieht:

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

Was ist das?

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

FreeBSD und OSX eine Version von NetBSD statderived haben.

Sie können die Ausgabe mit Format-Schalter einstellen (siehe die Handbuchseiten auf die Links oben).

%  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

Einige OS X-Versionen von stat können die -f%R Option für Formate fehlen. In diesem Fall kann -stat -f%Y ausreichen. Die -f%Y Option wird das Ziel eines Symlink zeigen, während -f%R zeigt den absoluten Pfad zur Datei entspricht.

EDIT:

Wenn Sie in der Lage sind, verwenden Perl (Darwin / O X mit dem jüngsten verions von perl installiert kommt), dann:

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

funktioniert.

Hier ist eine portable Shell-Funktion, die funktioniert in sollte ANY Bourne vergleichbar Shell. Es wird die relative Pfad Zeichensetzung „.. oder.“ Lösen und symbolische Links auflösen.

Wenn Sie aus irgendeinem Grund haben kein realpath (1) Befehl oder readlink (1) Dieses aliased werden kann.

which realpath || alias realpath='real_path'

Genießen Sie:

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

auch, nur für den Fall interessiert jemand hier ist, wie Basisnamen und dirname in 100% reinen Shell-Code zu implementieren:

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

Sie können an meinem Google-Website-Version dieses Shell-Code aktualisiert finden: http: // sites. google.com/site/jdisnard/realpath

EDIT: Dieser Code wird unter den Bedingungen der 2-Klausel (freeBSD Stil) Lizenz lizenziert. Eine Kopie der Lizenz kann durch Befolgen des oben Hyperlink zu meiner Website gefunden werden.

Der einfachste Weg, dieses Problem zu lösen und die Funktionalität von Readlink auf Mac aktiviert w / Homebrew installiert oder FreeBSD ist ‚coreutils‘ Paket zu installieren. Kann auch auf bestimmte Linux-Distributionen und anderen POSIX OS erforderlich.

Zum Beispiel in FreeBSD 11 installierte ich durch den Aufruf:

# pkg install coreutils

Auf MacOS mit Homebrew, würde der Befehl:

$ brew install coreutils

Nicht wirklich sicher, warum die anderen Antworten sind so kompliziert, das ist alles dort ist zu ihm. Die Dateien werden nicht an einem anderen Ort, sie sind doch nur nicht installiert ist.

Start aktualisieren

Das ist so ein häufiges Problem, das wir zusammen genannt gesetzt haben eine Bash 4 Bibliothek zur freien Verwendung (MIT-Lizenz) realpath-lib . Dies ist so konzipiert, zu emulieren readlink -f standardmäßig und zwei Testreihen, um zu überprüfen (1), dass es für ein bestimmtes Unix-System funktioniert und (2) gegen readlink -f wenn installiert (aber dies ist nicht erforderlich). Zusätzlich kann es verwendet werden, um zu untersuchen, zu identifizieren und entspannen tief, gebrochenen Symlinks und zirkuläre Referenzen, so kann es ein nützliches Werkzeug für die Diagnose von tief verschachtelten physikalischen oder symbolischen Verzeichnis- und Datei Problemen. Es kann unter github.com oder bitbucket.org .

Ende aktualisieren

Eine weitere sehr kompakte und effiziente Lösung, die nicht aber Bash auf etwas angewiesen ist:

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

}

Dazu gehört auch eine Umgebung no_symlinks Einstellung, die die Fähigkeit Symlinks auf das physikalische System zu lösen bietet. Solange no_symlinks auf etwas gesetzt, das heißt no_symlinks='on' dann Symlinks auf das physikalische System gelöst werden. Sonst werden sie (die Standardeinstellung) angewendet werden.

Dies sollte auf jedem System arbeiten, die Bash bietet, und eine Bash kompatibel Exit-Code für Testzwecke zurück.

Es gibt bereits viele Antworten, aber keine für mich gearbeitet ... Also das ist, was ich jetzt mit.

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

Besser spät als nie, nehme ich an. Ich war motiviert, dies speziell zu entwickeln, weil mein Fedora-Skripte wurde nicht auf dem Mac arbeitet. Das Problem ist, Abhängigkeiten und Bash. Macs haben sie nicht, oder wenn sie es tun, sind sie oft woanders (ein anderer Pfad). Dependency Pfad Manipulation in einem Cross-Plattform-Bash-Skript ist ein Kopfschmerz am besten und ein Sicherheitsrisiko im schlimmsten Fall -. So ist es am besten, ihre Verwendung zu vermeiden, wenn möglich

Die Funktion get_realpath () unten ist einfach, Bash-centric, und keine Abhängigkeiten erforderlich sind. Ich verwende nur die Bash builtins echo und cd . Es ist auch ziemlich sicher, da alles, was in jeder Phase der Art und Weise getestet wird, und es gibt Fehlerbedingungen.

Wenn Sie nicht wollen, Symlinks folgen, dann setzen gesetzt -P an der Vorderseite des Skripts, aber ansonsten cd sollte die symbolischen Links standardmäßig lösen. Es wurde mit Datei Argumente geprüft, die {absolut sind | relativ | Symlink | local} und es gibt den absoluten Pfad zur Datei. Bisher haben wir keine Probleme mit ihm gehabt.

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

}

Sie können dies kombinieren mit anderen Funktionen get_dirname, get_filename, get_stemname und validate_path. Diese können in unserem GitHub-Repository gefunden werden, wie realpath-lib (vollständige Offenlegung - das ist unser Produkt aber wir bieten es kostenlos für die Gemeinschaft ohne Einschränkungen). Es ist auch als Lehr-Instrument dienen könnte -. Es ist gut dokumentiert

Wir haben unser Bestes versucht, so genannte Praktiken ‚moderne Bash‘ zu bewerben, aber Bash ist ein großes Thema, und ich bin sicher, dass es immer Raum für Verbesserungen sein. Es erfordert Bash 4+ kann aber gemacht wird mit älteren Versionen zu arbeiten, wenn sie immer noch rund.

Truely plattform indpendent wäre auch der R-onliner

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

Um tatsächlich imitiert readlink -f <path>, 2 $ anstelle von $ 1 müßte genutzt werden.

Da meine Arbeit von Menschen mit nicht-BSD Linux verwendet wird, sowie macOS, ich habe für die Verwendung dieser Aliase in unserem Build-Skripte entschieden (sed enthielt, da sie ähnliche Probleme haben):

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

Optional überprüfen Sie automatisch hinzufügen könnte Homebrew + coreutils Abhängigkeiten:

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

Ich nehme wirklich sein „global“ es braucht andere zu überprüfen ... aber das kommt wahrscheinlich in der Nähe der 80/20-Marke.

Ich schrieb einen realpath Dienstprogramm für OS X , die die gleichen Ergebnisse wie readlink -f zur Verfügung stellen können.


Hier ist ein Beispiel:

(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


Wenn Sie MacPorts verwenden, können Sie es mit dem folgenden Befehl installieren:. sudo port selfupdate && sudo port install realpath

Erklärung

coreutils ist ein brew Paket für die Installation von GNU/Linux-core utilities, die entsprechen, um die Mac OSX Umsetzung von Ihnen, so dass Sie verwenden können diese

Sie können Programme zu finden, oder utilties auf Ihrem mac OS x-system, die scheinen ähnlich zu Linux coreutils ("Core-Utilities"), doch unterscheiden Sie sich in einigen Arten (wie zum Beispiel mit verschiedenen Flaggen).

Dies ist, weil das Mac OSX die Implementierung dieser tools sind unterschiedlich.Um die original-GNU - /Linux-artiges Verhalten, können Sie installieren die coreutils über das Paket brew Paket-management-system.

Dies installiert die entsprechenden core utilities, vorangestellt g.E. g.für readlink, Sie finden eine entsprechende greadlink Programm.

Um readlink führen Sie wie den GNU readlink (greadlink) die Durchführung, Sie können einen einfachen alias nach der Installation von coreutils.

Umsetzung

  1. Installieren brew

Befolgen Sie die Anweisungen auf https://brew.sh/

  1. Installieren von coreutils-Paket

brew install coreutils

  1. Erstellen eines Alias

Sie können Ihren alias in ~/.bashrc, ~/.bash_profile, oder wo auch immer Sie sind verwendet zu halten Sie Ihre bash-Aliase.Ich persönlich halte mir in ~/.bashrc

alias readlink=greadlink

Ähnliches können Sie Aliase für andere coreutils wie gmv, gdu, gdf, und so auf.Aber Vorsicht, dass die GNU-Verhalten auf einem mac-Computer kann verwirrend sein, zum anderen arbeitet mit nativen coreutils, oder verhält sich unerwartet auf Ihrem mac-system.

Dies ist, was ich benutze:

stat -f %N $your_path

Die Wege zu readlink unterscheiden sich zwischen meinem System und verkaufen. Bitte versuchen Sie den vollständigen Pfad angeben:

  

/sw/sbin/readlink -f

Perl hat eine Readlink Funktion (zB Wie kopiere ich symbolische Links in Perl? ). Dies funktioniert in den meisten Plattformen, einschließlich OS X:

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

Zum Beispiel:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

Die Antwort von @Keith Smith gibt eine unendliche Schleife.

Hier ist meine Antwort, die ich nur auf SunOS verwenden (SunOS verpassen so viele POSIX und GNU-Befehle).

Es ist eine Skriptdatei, die Sie in einem Ihrer $ PATH Verzeichnisse zu setzen haben:

#!/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"
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top