Wie kann ich das Verhalten von GNU ' s readlink -f auf einem Mac?
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 ...]
Lösung
readlink -f
macht zwei Dinge:
- Es iteriert entlang einer Folge von Symlinks, bis es eine tatsächliche Datei findet.
- 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.
- Install Homebrew
- Ausführen "Gebräu installieren coreutils"
- 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 stat
derived 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
- Installieren brew
Befolgen Sie die Anweisungen auf https://brew.sh/
- Installieren von coreutils-Paket
brew install coreutils
- 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"