Bei einem gegebenen absoluten oder relativen Pfad (in einem Unix-ähnlichen System) möchte ich den vollständigen Pfad des Ziels nach dem Auflösen von Zwischensymlinks bestimmen. Bonuspunkte für das gleichzeitige Auflösen der ~ Benutzernamen-Notation.
Wenn das Ziel ein Verzeichnis ist, ist es möglicherweise möglich, chdir () in das Verzeichnis zu übertragen und dann getcwd () aufzurufen, aber ich möchte dies wirklich über ein Shell-Skript tun, anstatt einen C-Helfer zu schreiben. Leider neigen Shells dazu, die Existenz von Symlinks vor dem Benutzer zu verbergen (dies ist ein Bash unter OS X):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
Was ich möchte, ist eine Funktion auflösen (), so dass bei Ausführung aus dem Verzeichnis tmp im obigen Beispiel auflösen ("foo") == "/ Users / greg / tmp / bar".
cd
es zuerst tun, bevor Sie anrufenpwd -P
. Mit anderen Worten: es erlaubt Sie nicht zu lösen Symlinks (das Ziel sehen) , um Dateien oder gebrochen Symlinks, und für die Lösung bestehenden Verzeichnis Symlinks Sie zusätzliche Arbeit zu tun haben (das vorherige Arbeitsverzeichnis wiederherstellen oder die Lokalisierungcd
undpwd -P
Anrufe in einer Unterschale).Anmerkung des Herausgebers: Das Obige funktioniert mit GNU
readlink
und FreeBSD / PC-BSD / OpenBSDreadlink
, jedoch nicht unter OS X ab 10.11.GNU
readlink
bietet zusätzliche, verwandte Optionen, z. B.-m
zum Auflösen eines Symlinks, unabhängig davon, ob das endgültige Ziel vorhanden ist oder nicht.Beachten Sie, dass seit GNU coreutils 8.15 (2012-01-06) ein Realpath- Programm verfügbar ist, das weniger stumpf und flexibler als das oben genannte ist. Es ist auch kompatibel mit dem gleichnamigen FreeBSD-Dienstprogramm. Es enthält auch Funktionen zum Generieren eines relativen Pfads zwischen zwei Dateien.
[Admin-Zusatz unten aus Kommentar von halloleo - danorton]
Verwenden Sie für Mac OS X (bis mindestens 10.11.x)
readlink
ohne die-f
Option:Anmerkung des Herausgebers: Dadurch werden Symlinks nicht rekursiv aufgelöst und das endgültige Ziel wird nicht gemeldet . Wenn beispielsweise ein Symlink angegeben wird
a
, auf den verweistb
, auf den wiederum verwiesen wird,c
wird dies nur gemeldetb
(und es wird nicht sichergestellt, dass es als absoluter Pfad ausgegeben wird ).Verwenden Sie den folgenden
perl
Befehl unter OS X, um die Lücke der fehlendenreadlink -f
Funktionalität zu schließen:perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"
quelle
readlink
funktioniert unter OSX, benötigt aber eine andere Syntax:readlink $path
ohne die-f
."pwd -P" scheint zu funktionieren, wenn Sie nur das Verzeichnis wollen, aber wenn Sie aus irgendeinem Grund den Namen der tatsächlichen ausführbaren Datei wollen, denke ich nicht, dass das hilft. Hier ist meine Lösung:
quelle
Einer meiner Favoriten ist
realpath foo
quelle
realpath
auf Centos 6 mit GNU Coreutils 8.4.31. Ich bin unter Unix und Linux auf mehrere andere gestoßen , die einen GNU-Coreutils ohne gepackt habenrealpath
. Es scheint also von mehr als nur der Version abhängig zu sein.realpath
überreadlink
weil es Optionsflags bietet wie--relative-to
scheint genau das zu sein, wonach Sie fragen - es akzeptiert einen willkürlichen Pfad, löst alle Symlinks auf und gibt den "echten" Pfad zurück - und es ist "Standard * nix", den wahrscheinlich alle Systeme bereits haben
quelle
Ein anderer Weg:
quelle
Wenn Sie einige der angegebenen Lösungen zusammenstellen und wissen, dass Readlink auf den meisten Systemen verfügbar ist, aber unterschiedliche Argumente benötigt, funktioniert dies unter OSX und Debian gut. Bei BSD-Systemen bin ich mir nicht sicher. Möglicherweise muss die Bedingung sein
[[ $OSTYPE != darwin* ]]
,-f
nur von OSX auszuschließen .quelle
So können Sie den tatsächlichen Pfad zur Datei in MacOS / Unix mithilfe eines Inline-Perl-Skripts ermitteln:
So erhalten Sie das Verzeichnis einer verknüpften Datei:
quelle
Ist Ihr Pfad ein Verzeichnis oder eine Datei? Wenn es ein Verzeichnis ist, ist es einfach:
Wenn es sich jedoch möglicherweise um eine Datei handelt, funktioniert dies nicht:
weil der Symlink möglicherweise in einen relativen oder vollständigen Pfad aufgelöst wird.
Bei Skripten muss ich den tatsächlichen Pfad finden, damit ich auf die Konfiguration oder andere zusammen damit installierte Skripte verweisen kann. Ich verwende Folgendes:
Sie können einen
SOURCE
beliebigen Dateipfad festlegen . Solange der Pfad ein Symlink ist, wird dieser Symlink grundsätzlich aufgelöst. Der Trick befindet sich in der letzten Zeile der Schleife. Wenn der aufgelöste Symlink absolut ist, wird er als verwendetSOURCE
. Wenn es jedoch relativ ist, wird das dafür vorangestelltDIR
, das durch den einfachen Trick, den ich zuerst beschrieben habe, in einen realen Ort aufgelöst wurde.quelle
quelle
Hinweis: Ich glaube, dass dies eine solide, tragbare und vorgefertigte Lösung ist, die aus genau diesem Grund immer langwierig ist .
Im Folgenden finden Sie ein vollständig POSIX-kompatibles Skript / eine Funktion , die plattformübergreifend ist (funktioniert auch unter macOS, das ab 10.12 (Sierra)
readlink
immer noch nicht unterstützt wird-f
). Es werden nur POSIX-Shell-Sprachfunktionen und nur POSIX-kompatible Dienstprogrammaufrufe verwendet .Es ist eine tragbare Implementierung von GNUs
readlink -e
(der strengeren Version vonreadlink -f
).Sie können das ausführen Skript mit
sh
oder beziehen Sie die Funktion inbash
,ksh
undzsh
:In einem Skript können Sie es beispielsweise wie folgt verwenden, um das wahre Ursprungsverzeichnis des laufenden Skripts mit aufgelösten Symlinks abzurufen:
rreadlink
Skript- / Funktionsdefinition:Der Code wurde mit Dankbarkeit aus dieser Antwort angepasst .
Ich habe auch eine erstelltes
bash
-basierte Stand-alone - Programm - Version hier , die Sie mit installieren könnennpm install rreadlink -g
, wenn Sie Node.js installiert.Eine Tangente an die Sicherheit:
jarno fragt in Bezug auf die Funktion, die sicherstellt, dass Builtin
command
nicht von einem gleichnamigen Alias oder einer Shell-Funktion überschattet wird, in einem Kommentar:Die Motivation, um
rreadlink
sicherzustellen, dass diescommand
seine ursprüngliche Bedeutung hat, besteht darin, es zu verwenden, um (harmlose) Komfort- Aliase und -Funktionen zu umgehen, die häufig zum Schattieren von Standardbefehlen in interaktiven Shells verwendet werden, z. B. die Neudefinitionls
, um bevorzugte Optionen einzuschließen.Ich denke , es ist sicher zu sagen , dass es sei denn , Sie ist der Umgang mit einer nicht vertrauenswürdigen, bösartigen Umgebung, mich darum zu kümmern
unalias
oderunset
- oder, was das betrifft,while
,do
, ... - neu definiert wird kein Problem.Es gibt etwas , auf das sich die Funktion verlassen muss, um ihre ursprüngliche Bedeutung und ihr Verhalten zu erhalten - daran führt kein Weg vorbei.
Dass POSIX-ähnliche Shells die Neudefinition von integrierten Funktionen und sogar von Sprachschlüsselwörtern ermöglichen, ist von Natur aus ein Sicherheitsrisiko (und das Schreiben von paranoidem Code ist im Allgemeinen schwierig).
Um Ihre Bedenken speziell anzusprechen:
Die Funktion beruht auf
unalias
undunset
hat ihre ursprüngliche Bedeutung. Es wäre ein Problem, wenn sie in einer Weise als Shell-Funktionen neu definiert würden, die ihr Verhalten verändert. Die Neudefinition als Alias ist nicht unbedingt ein Problem, da das Zitieren (eines Teils) des Befehlsnamens (z. B.\unalias
) Aliase umgeht.Jedoch unter Angabe ist nicht eine Option für die Shell - Schlüsselwörter (
while
,for
,if
,do
, ...) und während Shell keywords tun hat Vorrang vor Shell - Funktionen , inbash
undzsh
Aliase hat die höchste Priorität, so zum Schutz vor Shell-Schlüsselwort Neudefinitionen Sie ausführen müssen ,unalias
mit Ihre Namen (obwohl in nicht interaktivenbash
Shells (wie Skripten) Aliase standardmäßig nicht erweitert werden - nur wennshopt -s expand_aliases
sie zuerst explizit aufgerufen werden).Um sicherzustellen, dass
unalias
- als eingebautes - seine ursprüngliche Bedeutung hat, müssen Sie\unset
es zuerst verwenden, was erfordert, dassunset
es seine ursprüngliche Bedeutung hat:unset
ist eine eingebaute Shell. Um sicherzustellen, dass sie als solche aufgerufen wird, müssen Sie sicherstellen, dass sie selbst nicht als Funktion neu definiert wird . Während Sie ein Alias-Formular mit Anführungszeichen umgehen können, können Sie ein Shell-Funktionsformular nicht umgehen - catch 22.Daher
unset
kann es keine garantierte Möglichkeit geben, sich gegen alle böswilligen Neudefinitionen zu verteidigen, es sei denn, Sie können sich darauf verlassen , dass sie ihre ursprüngliche Bedeutung haben.quelle
[
als Alias indash
undbash
und als Shell-Funktion in definieren kannbash
.while
als eine Funktion inbash
,ksh
undzsh
(aber nichtdash
), aber nur mitfunction <name>
Syntax:function while { echo foo; }
Werke (while() { echo foo; }
nicht). Dies wird daswhile
Schlüsselwort jedoch nicht beschatten , da Schlüsselwörter eine höhere Priorität als Funktionen haben (die einzige Möglichkeit, diese Funktion aufzurufen, ist as\while
). Inbash
undzsh
haben Aliase eine höhere Priorität als Schlüsselwörter, daher werden sie durch Alias- Neudefinitionen von Schlüsselwörtern beschattet (bash
standardmäßig jedoch nur in interaktiven Shells, sofern dies nichtshopt -s expand_aliases
ausdrücklich aufgerufen wird).Gängige Shell-Skripte müssen häufig ihr "Home" -Verzeichnis finden, auch wenn sie als Symlink aufgerufen werden. Das Skript muss also seine "echte" Position ab nur $ 0 finden.
Auf meinem System wird ein Skript gedruckt, das Folgendes enthält. Dies sollte ein guter Hinweis darauf sein, was Sie benötigen.
quelle
Versuche dies:
quelle
Da ich im Laufe der Jahre oft darauf gestoßen bin und dieses Mal eine reine Bash-Portable-Version brauchte, die ich unter OSX und Linux verwenden konnte, schrieb ich eine:
Die lebende Version lebt hier:
https://github.com/keen99/shell-functions/tree/master/resolve_path
Aber um SO willen, hier ist die aktuelle Version (ich denke, sie ist gut getestet. Aber ich bin offen für Feedback!)
Es mag nicht schwierig sein, es für eine einfache Bourne-Shell (sh) zum Laufen zu bringen, aber ich habe es nicht versucht ... Ich mag $ FUNCNAME zu sehr. :) :)
Hier ist ein klassisches Beispiel, dank Brew:
Verwenden Sie diese Funktion und es wird der -real- Pfad zurückgegeben:
quelle
Um die Mac-Inkompatibilität zu umgehen, habe ich mir etwas ausgedacht
Nicht großartig, aber OS-übergreifend
quelle
python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"
wäre daher wahrscheinlich sinnvoller. Wenn Sie Python aus einem Shell-Skript verwenden müssen, sollten Sie wahrscheinlich nur Python verwenden und sich die Kopfschmerzen des plattformübergreifenden Shell-Skripts ersparen.dirname
,basename
undreadlink
sind externe Dienstprogramme , nicht Einbauten Shell;dirname
undbasename
sind Teil von POSIX,readlink
ist nicht.php
jedoch mit OS X kommt, auch wenn der Körper der Frage Nennungen OS X, ist es nicht als solche markiert, und es ist deutlich geworden , Da Menschen auf verschiedenen Plattformen hierher kommen, um Antworten zu erhalten, sollten Sie darauf hinweisen, was plattformspezifisch / nicht POSIX ist.Dies ist ein Symlink-Resolver in Bash, der unabhängig davon funktioniert, ob es sich bei dem Link um ein Verzeichnis oder um ein Nicht-Verzeichnis handelt:
Beachten Sie, dass alle
cd
undset
Sachen in einer Subshell stattfinden.quelle
{}
um das()
sind nicht unnötig, wenn()
hinter dem Funktionsnamen keine stehen . Bash akzeptiert Funktionsdeklarationen ohne,()
da die Shell keine Parameterlisten in Deklarationen enthält und keine Aufrufe mit macht,()
sodass Funktionsdeklarationen mit()
nicht viel Sinn machen.Hier präsentiere ich eine meiner Meinung nach plattformübergreifende Lösung (zumindest Linux und MacOS) für die Antwort, die derzeit für mich gut funktioniert.
Hier ist eine macOS (nur?) Lösung. Möglicherweise besser für die ursprüngliche Frage geeignet.
quelle
Meine Antwort hier Bash: Wie bekomme ich einen echten Pfad für einen Symlink?
aber kurz gesagt sehr praktisch in Skripten:
Diese sind Teil von GNU-Coreutils, die für die Verwendung in Linux-Systemen geeignet sind.
Um alles zu testen, setzen wir symlink in / home / test2 /, ändern einige zusätzliche Dinge und führen / rufen es aus dem Stammverzeichnis auf:
Wo
quelle
Wenn pwd nicht verwendet werden kann (z. B. das Aufrufen eines Skripts von einem anderen Speicherort aus), verwenden Sie realpath (mit oder ohne Verzeichnisname):
Funktioniert sowohl beim Aufrufen über (mehrere) Symlinks als auch beim direkten Aufrufen des Skripts - von jedem Ort aus.
quelle