Ist es wichtig, ob /foo/baroder sogar /footatsächlich vorhanden, oder interessieren Sie sich nur für den Aspekt der Zeichenfolgenmanipulation gemäß den Pfadnamenregeln?
Chen Levy
5
@ Walwal ... das ist irgendwie erfunden ...
Camilo Martin
2
@CamiloMartin Überhaupt nicht erfunden - es macht genau das, was die Frage stellt - transformiert sich /foo/bar/..in /fooeinen bashBefehl und verwendet ihn . Wenn es andere Anforderungen gibt, die nicht angegeben sind, dann sollten sie vielleicht ...
Twalberg
14
@twalberg Sie haben zu viel TDD -_- '
Camilo Martin
Antworten:
192
Wenn Sie einen Teil eines Dateinamens aus dem Pfad entfernen möchten, sind "dirname" und "basename" Ihre Freunde, und "realpath" ist ebenfalls praktisch.
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath Alternativen
Wenn realpathIhre Shell dies nicht unterstützt, können Sie es versuchen
readlink -f /path/here/..
Ebenfalls
readlink -m /path/there/../../
Funktioniert genauso wie
realpath -s /path/here/../../
, dass der Pfad nicht existieren muss, um normalisiert zu werden.
Für diejenigen unter Ihnen, die eine OS X-Lösung benötigen, lesen Sie die Antwort von Adam Liss unten.
Trenton
stackoverflow.com/a/17744637/999943 Dies ist eine stark verwandte Antwort! Ich bin an einem Tag auf diese beiden QS-Beiträge gestoßen und wollte sie miteinander verknüpfen.
Beide realpathund readlinkstammen von den GNU-Kerndienstprogrammen, sodass Sie höchstwahrscheinlich entweder beide oder keine erhalten. Wenn ich mich richtig erinnere, unterscheidet sich die Readlink-Version auf dem Mac geringfügig von der GNU-Version: - \
Antoine 'hashar' Musso
100
Ich weiß nicht, ob es einen direkten Bash-Befehl gibt, aber ich tue es normalerweise
Dadurch werden Softlinks normalisiert, aber nicht aufgelöst. Dies kann entweder ein Fehler oder eine Funktion sein. :-)
Adam Liss
4
Es gibt auch ein Problem, wenn $ CDPATH definiert ist. weil die "cd foo" in jedes "foo" -Verzeichnis wechselt, das ein Unterverzeichnis von $ CDPATH ist, nicht nur in ein "foo", das sich im aktuellen Verzeichnis befindet. Ich denke, Sie müssen etwas tun wie: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs
7
Tims Antwort ist definitiv die einfachste und tragbarste. CDPATH ist einfach zu handhaben: dir = "$ (nicht gesetztes CDPATH && cd" $ dir "&& pwd)"
David Blevins
2
Dies kann sehr gefährlich sein ( rm -rf $normalDir), wenn dirToNormalize nicht existiert!
Frank Meulenaar
4
Ja, es ist wahrscheinlich am besten, einen &&Kommentar gemäß @ DavidBlevins zu verwenden.
Elias Dorneles
54
Versuchen Sie es realpath. Nachfolgend finden Sie die Quelle in ihrer Gesamtheit, die hiermit der Öffentlichkeit zugänglich gemacht wurde.
// realpath.c: display the absolute path to a file or directory.// Adam Liss, August, 2007// This program is provided "as-is" to the public domain, without express or// implied warranty, for any non-profit use, provided this notice is maintained.#include<stdio.h>#include<stdlib.h>#include<string.h>#include<libgen.h>#include<limits.h>staticchar*s_pMyName;void usage(void);int main(int argc,char*argv[]){char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));if(argc <2)
usage();
printf("%s\n", realpath(argv[1], sPath));return0;}void usage(void){
fprintf(stderr,"usage: %s PATH\n", s_pMyName);
exit(1);}
Es ist Standard auf Ubuntu / BSD, nicht Centos / OSX
Erik Aronesty
4
Sie sollten den Teil wirklich mit "Bonus: Es ist ein Bash-Befehl und überall verfügbar!" Durchstreichen. Teil über realpath. Es ist auch mein Favorit, aber anstatt Ihr Tool von der Quelle zu ergänzen. Es ist alles zu schwer, und die meiste Zeit willst du nur readlink -f... Übrigens, es readlinkist auch kein eingebauter Bash, sondern Teil von coreutilsUbuntu.
Tomasz Gandor
4
Sie haben gesagt, Sie spenden dies an die Public Domain, aber in der Kopfzeile steht auch "für jede gemeinnützige Verwendung" - wollen Sie es wirklich an die Public Domain spenden? Das würde bedeuten, dass es sowohl für kommerzielle Zwecke als auch für gemeinnützige Zwecke verwendet werden kann…
me_and
36
Eine tragbare und zuverlässige Lösung ist die Verwendung von Python, das fast überall vorinstalliert ist (einschließlich Darwin). Sie haben zwei Möglichkeiten:
abspath Gibt einen absoluten Pfad zurück, löst jedoch keine Symlinks auf:
Danke, das war der einzige, der funktioniert hat. Readlink oder Realpath sind unter OS X nicht verfügbar. Python sollte auf den meisten Plattformen verfügbar sein.
Sorin
1
Nur zur Verdeutlichung, readlinkist unter OS X verfügbar, nur nicht mit der -fOption. Tragbare Abhilfen diskutiert hier .
StvnW
3
Es verwirrt mich buchstäblich, dass dies die einzig vernünftige Lösung ist, wenn Sie keinen Links folgen möchten. Der Unix-Weg mein FUSS.
DannoHung
34
Verwenden Sie das Dienstprogramm readlink aus dem Paket coreutils.
BSD hat nicht das Flag -f, was bedeutet, dass dies selbst auf dem neuesten MacOS Mojave und vielen anderen Systemen fehlschlägt. Vergessen Sie die Verwendung von -f, wenn Sie Portabilität wünschen, da viele Betriebssysteme betroffen sind.
Sorin
1
@ Sorin. Die Frage betrifft nicht den Mac, sondern Linux.
Mattalxndr
14
readlinkist der Bash-Standard zum Erhalten des absoluten Pfades. Es hat auch den Vorteil, leere Zeichenfolgen zurückzugeben, wenn Pfade oder ein Pfad nicht vorhanden sind (vorausgesetzt, die Flags dazu).
Verwenden Sie Folgendes, um den absoluten Pfad zu einem Verzeichnis zu erhalten, das möglicherweise vorhanden ist oder nicht, dessen Eltern jedoch vorhanden sind:
abspath=$(readlink -f $path)
So erhalten Sie den absoluten Pfad zu einem Verzeichnis, das zusammen mit allen Eltern vorhanden sein muss:
abspath=$(readlink -e $path)
Um den angegebenen Pfad zu kanonisieren und Symlinks zu folgen, falls vorhanden, aber ansonsten fehlende Verzeichnisse zu ignorieren und den Pfad trotzdem zurückzugeben, ist es:
abspath=$(readlink -m $path)
Der einzige Nachteil ist, dass Readlink Links folgt. Wenn Sie Links nicht folgen möchten, können Sie diese alternative Konvention verwenden:
abspath=$(cd ${path%/*}&& echo $PWD/${path##*/})
Dadurch wird der Verzeichnisteil von $ path geändert und das aktuelle Verzeichnis zusammen mit dem Dateiteil von $ path gedruckt. Wenn chdir nicht funktioniert, erhalten Sie eine leere Zeichenfolge und einen Fehler auf stderr.
readlinkist eine gute Option, wenn es verfügbar ist. Die OS X-Version unterstützt die Optionen -eoder nicht -f. In den ersten drei Beispielen sollten doppelte Anführungszeichen vorhanden sein $path, um Leerzeichen oder Platzhalter im Dateinamen zu verarbeiten. +1 für die Parametererweiterung, dies hat jedoch eine Sicherheitslücke. Wenn pathes leer ist, wird dies cdin Ihr Home-Verzeichnis verschoben. Sie benötigen doppelte Anführungszeichen. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Toxalot
Dies war nur ein Beispiel. Wenn Sie auf Sicherheit aus sind, sollten Sie Bash oder eine andere Shell-Variante überhaupt nicht verwenden. Außerdem hat bash seine eigenen Probleme, wenn es um plattformübergreifende Kompatibilität geht, sowie Probleme mit Funktionsänderungen zwischen Hauptversionen. OSX ist nur eine von vielen Plattformen mit Problemen im Zusammenhang mit Shell-Skripten, ganz zu schweigen davon, dass es auf BSD basiert. Wenn Sie wirklich plattformübergreifend sein müssen, müssen Sie POSIX-kompatibel sein, damit die Parametererweiterung wirklich aus dem Fenster geht. Schauen Sie sich einige Zeit Solaris oder HP-UX an.
Craig
6
Es ist wichtig, hier keine Beleidigung zu bedeuten, aber auf obskure Themen wie diese hinzuweisen. Ich möchte nur eine schnelle Antwort auf dieses triviale Problem und hätte diesem Code mit allen Eingaben vertraut, wenn der obige Kommentar nicht gewesen wäre. Es ist auch wichtig, OS-X in diesen Bash-Diskussionen zu unterstützen. Es gibt viele Befehle, die unter OS-X leider nicht unterstützt werden, und viele Foren halten dies für selbstverständlich, wenn es um Bash geht. Dies bedeutet, dass wir weiterhin viele plattformübergreifende Probleme haben werden, es sei denn, sie werden eher früher als später behandelt.
Rebs
13
Alte Frage, aber es gibt einen viel einfacheren Weg, wenn Sie mit vollständigen Pfadnamen auf Shell-Ebene arbeiten:
abspath = "$ (cd" $ path "&& pwd)"
Da die CD in einer Subshell ausgeführt wird, hat dies keine Auswirkungen auf das Hauptskript.
Angenommen, Ihre in die Shell integrierten Befehle akzeptieren -L und -P, sind zwei Varianten:
Persönlich brauche ich diesen späteren Ansatz nur selten, wenn ich aus irgendeinem Grund von symbolischen Links fasziniert bin.
Zu Ihrer Information: Variation beim Abrufen des Startverzeichnisses eines Skripts, die auch dann funktioniert, wenn das Skript später das aktuelle Verzeichnis ändert.
Die Verwendung von CD stellt sicher, dass Sie immer das absolute Verzeichnis haben, auch wenn das Skript von Befehlen wie ./script.sh ausgeführt wird, die ohne cd / pwd oft nur ... Unnütz sind, wenn das Skript später eine CD erstellt.
Wie Adam Liss feststellte, realpathwird nicht mit jeder Distribution gebündelt. Was schade ist, denn es ist die beste Lösung. Der bereitgestellte Quellcode ist großartig und ich werde ihn wahrscheinlich jetzt verwenden. Folgendes habe ich bisher verwendet, das ich hier der Vollständigkeit halber teile:
get_abs_path(){local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd ->/dev/null
echo "$ABS_PATH"}
Wenn Sie es zu lösen Symlinks möchten, ersetzen Sie einfach pwdmit pwd -P.
Ein Gotcha mit der pwd -POption für diesen Fall ... Überlegen Sie, was passieren würde, wenn $(basename "$1")ein Symlink zu einer Datei in einem anderen Verzeichnis wäre. Der pwd -Peinzige löst Symlinks im Verzeichnisbereich des Pfads auf, nicht jedoch im Basisnamenbereich.
Ich vermute, dass dies fehlschlägt, wenn das Argument kein Verzeichnis ist. Angenommen, ich möchte wissen, wohin / usr / bin / java führt?
Edward Falk
1
Wenn Sie wissen, dass es sich um eine Datei handelt, können Sie es pushd $(dirname /usr/bin/java)versuchen.
schmunk
5
Nicht gerade eine Antwort, aber vielleicht eine Folgefrage (die ursprüngliche Frage war nicht explizit):
readlinkist in Ordnung, wenn Sie tatsächlich Symlinks folgen möchten. Es gibt aber auch einen Anwendungsfall für das bloße Normalisieren von ./und ../und //Sequenzen, der rein syntaktisch durchgeführt werden kann, ohne Symlinks zu kanonisieren. readlinkist nicht gut dafür und auch nicht realpath.
for f in $paths;do(cd $f; pwd);done
funktioniert für vorhandene Pfade, bricht aber für andere.
Ein sedSkript scheint eine gute Wahl zu sein, außer dass Sie Sequenzen ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) nicht iterativ ersetzen können, ohne etwas wie Perl zu verwenden, das nicht auf allen Systemen sicher angenommen werden kann, oder eine hässliche Schleife zu verwenden, um die Ausgabe von sedto zu vergleichen seine Eingabe.
FWIW, ein Einzeiler mit Java (JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpatheine hat -sOption, keine symbolischen Links zu lösen und nur Entschlossenheit Verweise auf /./, /../und zusätzliche entfernen /Zeichen. In Kombination mit der -mOption wird realpathnur der Dateiname verarbeitet und keine tatsächliche Datei berührt. Es klingt nach der perfekten Lösung. Aber leider realpathfehlt immer noch auf vielen Systemen.
Toxalot
Das Entfernen von ..Komponenten kann nicht syntaktisch erfolgen, wenn Symlinks beteiligt sind. /one/two/../threewie ist nicht das gleiche , /one/threewenn twoein Symlink ist /foo/bar.
jrw32982 unterstützt Monica
@ jrw32982 Ja, wie ich in meiner Antwort sagte, ist dies für einen Anwendungsfall, in dem eine Symlink-Kanonisierung nicht gewünscht oder benötigt wird.
Jesse Glick
@JesseGlick Es geht nicht nur darum, ob Sie Symlinks kanonisieren möchten oder nicht. Ihr Algorithmus liefert tatsächlich die falsche Antwort. Damit Ihre Antwort richtig ist, müssen Sie a priori wissen, dass keine Symlinks vorhanden sind (oder dass sie nur eine bestimmte Form haben). Ihre Antwort besagt, dass Sie sie nicht kanonisieren möchten , nicht, dass der Pfad keine Symlinks enthält.
jrw32982 unterstützt Monica
Es gibt Anwendungsfälle, in denen die Normalisierung durchgeführt werden muss, ohne eine feste, vorhandene Verzeichnisstruktur anzunehmen. Die URI-Normalisierung ist ähnlich. In diesen Fällen ist es eine inhärente Einschränkung, dass das Ergebnis im Allgemeinen nicht korrekt ist, wenn sich in der Nähe eines Verzeichnisses, in dem das Ergebnis später angewendet wird, Symlinks befinden.
Jesse Glick
5
Ich bin zu spät zur Party, aber dies ist die Lösung, die ich entwickelt habe, nachdem ich eine Reihe von Themen wie diese gelesen habe:
resolve_dir(){(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"`2>/dev/null;if[ $?-eq 0];then pwd;fi)}
Dies wird den absoluten Pfad von $ 1 auflösen, gut mit ~ spielen, Symlinks in dem Pfad behalten, in dem sie sich befinden, und es wird nicht mit Ihrem Verzeichnisstapel in Konflikt geraten. Es gibt den vollständigen Pfad oder nichts zurück, wenn es nicht existiert. Es wird erwartet, dass 1 US-Dollar ein Verzeichnis ist, und es wird wahrscheinlich fehlschlagen, wenn dies nicht der Fall ist. Dies ist jedoch eine einfache Überprüfung, die Sie selbst durchführen können.
Gesprächige und etwas späte Antwort. Ich muss einen schreiben, da ich auf älterem RHEL4 / 5 stecke. Ich behandle absolute und relative Links und vereinfache //, /./ und somedir /../ Einträge.
Testen Sie unser neues Bash-Bibliotheksprodukt realpath-lib , das wir kostenlos und unbeschwert auf GitHub eingestellt haben. Es ist gründlich dokumentiert und ein großartiges Lernwerkzeug.
Es löst lokale, relative und absolute Pfade auf und hat keine Abhängigkeiten außer Bash 4+. es sollte also fast überall funktionieren. Es ist kostenlos, sauber, einfach und lehrreich.
function get_realpath(){if[[-f "$1"]]then# file *must* existif 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 locallocal tmppwd="$PWD"fielse# file *cannot* existreturn1# failurefi# reassemble realpath
echo "$tmppwd"/"${1##*/}"return0# success}
Es enthält auch Funktionen zu get_dirname, get_filename, get_ stemname und validate_path. Probieren Sie es plattformübergreifend aus und helfen Sie, es zu verbessern.
Dies löst jedoch keine Symlinks. Realpath verarbeitet Pfade, die an der Wurzel beginnen, und folgt im Verlauf Symlinks. Dies führt lediglich dazu, dass übergeordnete Referenzen reduziert werden.
Martijn Pieters
2
Basierend auf der Antwort von @ Andre könnte ich eine etwas bessere Version haben, falls jemand nach einer schleifenfreien, vollständig auf String-Manipulation basierenden Lösung sucht. Es ist auch nützlich für diejenigen, die keine Symlinks dereferenzieren möchten, was der Nachteil der Verwendung von realpathoder ist readlink -f.
Es funktioniert mit Bash-Versionen 3.2.25 und höher.
shopt -s extglob
normalise_path(){local path="$1"# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"# remove the last '/.'
echo "${path%%/.}"}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
Dies ist eine nette Idee, aber ich habe 20 Minuten damit verbracht, dies für verschiedene Versionen von Bash zum Laufen zu bringen. Es stellt sich heraus, dass die Extglob-Shell-Option aktiviert sein muss, damit dies funktioniert, und dies ist nicht standardmäßig der Fall. Wenn es um Bash-Funktionen geht, ist es wichtig, sowohl die erforderliche Version als auch nicht standardmäßige Optionen anzugeben, da diese Details zwischen den Betriebssystemen variieren können. Beispielsweise enthält eine neuere Version von Mac OSX (Yosemite) nur eine veraltete Version von bash (3.2).
Drwatsoncode
Entschuldigung @ricovox; Ich habe diese jetzt aktualisiert. Ich bin gespannt auf die genaue Version von Bash, die Sie dort haben. Die obige Formel (aktualisiert) funktioniert unter CentOS 5.8, das mit Bash 3.2.25
ϹοδεMεδιϲ
Entschuldigung für die Verwirrung. Dieser Code funktionierte auf meiner Version von Mac OSX Bash (3.2.57), nachdem ich extglob aktiviert hatte. Mein Hinweis zu Bash-Versionen war allgemeiner (was hier eher für eine andere Antwort in Bezug auf Regex in Bash gilt).
Drwatsoncode
2
Ich freue mich jedoch über Ihre Antwort. Ich habe es als Basis für meine eigenen benutzt. Im Übrigen sind mir mehrere Fälle aufgefallen, in denen Ihre fehlgeschlagen sind: (1) Relative Pfade hello/../world(2) Punkte im Dateinamen /hello/..world(3) Punkte nach dem doppelten Schrägstrich /hello//../world(4) Punkt vor oder nach dem doppelten Schrägstrich /hello//./worldoder /hello/.//world (5) Eltern nach dem aktuellen: /hello/./../world/ (6) Eltern after Parent: /hello/../../worldusw. - Einige davon können mithilfe einer Schleife behoben werden, um Korrekturen vorzunehmen, bis sich der Pfad nicht mehr ändert. (Entfernen Sie auch dir/../, nicht /dir/..aber entfernen Sie dir/..vom Ende.)
drwatsoncode
0
Basierend auf Loveborgs exzellentem Python-Snippet habe ich Folgendes geschrieben:
#!/bin/sh# Version of readlink that follows links to the end; good for Mac OS Xfor file in"$@";dowhile[-h "$file"];do
l=`readlink $file`case"$l"in/*) file="$l";;*) file=`dirname "$file"`/"$l"esacdone#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])""$file"done
Hier ist ein kurzer Testfall mit einigen verdrehten Leerzeichen in den Pfaden, um das Zitat vollständig auszuführen
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello">"/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt ""/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
Ich weiß, dass dies eine alte Frage ist. Ich biete immer noch eine Alternative an. Kürzlich bin ich auf dasselbe Problem gestoßen und habe keinen vorhandenen und portablen Befehl dafür gefunden. Also habe ich das folgende Shell-Skript geschrieben, das eine Funktion enthält, die den Trick ausführen kann.
#! /bin/sh function normalize {local rc=0local ret
if[ $# -gt 0 ] ; then# invalidif["x`echo $1 | grep -E '^/\.\.'`"!="x"];then
echo $1
return-1fi# convert to absolute pathif["x`echo $1 | grep -E '^\/'`"=="x"];then
normalize "`pwd`/$1"return $?fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`else
read line
normalize "$line"return $?fiif["x`echo $ret | grep -E '/\.\.?(/|$)'`"!="x"];then
ret=`normalize "$ret"`
rc=$?fi
echo "$ret"return $rc
}
Ich habe eine integrierte Funktion entwickelt, um dies zu handhaben, wobei der Schwerpunkt auf der höchstmöglichen Leistung liegt (zum Spaß). Symlinks werden nicht aufgelöst, daher ist es im Grunde dasselbe wie realpath -sm.
## A bash-only mimic of `realpath -sm`. ## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath (){
${*+false}&&{>&2 echo $FUNCNAME: missing operand;return1;};local c s p IFS='/';## path chunk, absolute path, input path, IFS for splitting paths into chunkslocal-i r=0;## return valuefor p in"$@";docase"$p"in## Check for leading backslashes, identify relative/absolute path'')((r|=1));continue;;//[!/]*)>&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem";((r|=2));continue;;/*);;*) p="$PWD/$p";;## Prepend the current directory to form an absolute pathesac
s='';for c in $p;do## Let IFS split the path at '/'scase $c in### NOTE: IFS is '/'; so no quotes needed here''|.);;## Skip duplicate '/'s and '/./'s..) s="${s%/*}";;## Trim the previous addition to the absolute path string*) s+=/$c;;### NOTE: No quotes here intentionally. They make no difference, it seemsesac;done;
echo "${s:-/}";## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` insteaddonereturn $r;}
Hinweis: Diese Funktion verarbeitet keine Pfade, die mit beginnen //, da genau zwei doppelte Schrägstriche am Anfang eines Pfads ein implementierungsdefiniertes Verhalten sind. Es behandelt jedoch /,/// und so weiter gut.
Diese Funktion scheint alle Randfälle richtig zu behandeln, aber es gibt möglicherweise noch einige, mit denen ich mich nicht befasst habe.
Leistungshinweis: Wird beim Aufrufen mit Tausenden von Argumenten abspathetwa 10-mal langsamer ausgeführt als realpath -sm; Wenn es mit einem einzelnen Argument aufgerufen wird, abspathläuft es> 110x schneller als realpath -smauf meinem Computer, hauptsächlich weil nicht jedes Mal ein neues Programm ausgeführt werden muss.
Die stat -f %N ~/DocumentsLinie ist ein roter Hering ... Ihre Schale ersetzt ~/Documentsdurch /Users/me/Documentsund statdruckt nur das Argument wörtlich.
/foo/bar
oder sogar/foo
tatsächlich vorhanden, oder interessieren Sie sich nur für den Aspekt der Zeichenfolgenmanipulation gemäß den Pfadnamenregeln?/foo/bar/..
in/foo
einenbash
Befehl und verwendet ihn . Wenn es andere Anforderungen gibt, die nicht angegeben sind, dann sollten sie vielleicht ...Antworten:
Wenn Sie einen Teil eines Dateinamens aus dem Pfad entfernen möchten, sind "dirname" und "basename" Ihre Freunde, und "realpath" ist ebenfalls praktisch.
realpath
AlternativenWenn
realpath
Ihre Shell dies nicht unterstützt, können Sie es versuchenEbenfalls
Funktioniert genauso wie
, dass der Pfad nicht existieren muss, um normalisiert zu werden.
quelle
realpath
undreadlink
stammen von den GNU-Kerndienstprogrammen, sodass Sie höchstwahrscheinlich entweder beide oder keine erhalten. Wenn ich mich richtig erinnere, unterscheidet sich die Readlink-Version auf dem Mac geringfügig von der GNU-Version: - \Ich weiß nicht, ob es einen direkten Bash-Befehl gibt, aber ich tue es normalerweise
und es funktioniert gut.
quelle
rm -rf $normalDir
), wenn dirToNormalize nicht existiert!&&
Kommentar gemäß @ DavidBlevins zu verwenden.Versuchen Sie es
realpath
. Nachfolgend finden Sie die Quelle in ihrer Gesamtheit, die hiermit der Öffentlichkeit zugänglich gemacht wurde.quelle
realpath
. Es ist auch mein Favorit, aber anstatt Ihr Tool von der Quelle zu ergänzen. Es ist alles zu schwer, und die meiste Zeit willst du nurreadlink -f
... Übrigens, esreadlink
ist auch kein eingebauter Bash, sondern Teil voncoreutils
Ubuntu.Eine tragbare und zuverlässige Lösung ist die Verwendung von Python, das fast überall vorinstalliert ist (einschließlich Darwin). Sie haben zwei Möglichkeiten:
abspath
Gibt einen absoluten Pfad zurück, löst jedoch keine Symlinks auf:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
Gibt einen absoluten Pfad zurück und löst dabei Symlinks auf, wodurch ein kanonischer Pfad generiert wird:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
In jedem Fall
path/to/file
kann entweder ein relativer oder ein absoluter Pfad sein.quelle
readlink
ist unter OS X verfügbar, nur nicht mit der-f
Option. Tragbare Abhilfen diskutiert hier .Verwenden Sie das Dienstprogramm readlink aus dem Paket coreutils.
quelle
readlink
ist der Bash-Standard zum Erhalten des absoluten Pfades. Es hat auch den Vorteil, leere Zeichenfolgen zurückzugeben, wenn Pfade oder ein Pfad nicht vorhanden sind (vorausgesetzt, die Flags dazu).Verwenden Sie Folgendes, um den absoluten Pfad zu einem Verzeichnis zu erhalten, das möglicherweise vorhanden ist oder nicht, dessen Eltern jedoch vorhanden sind:
So erhalten Sie den absoluten Pfad zu einem Verzeichnis, das zusammen mit allen Eltern vorhanden sein muss:
Um den angegebenen Pfad zu kanonisieren und Symlinks zu folgen, falls vorhanden, aber ansonsten fehlende Verzeichnisse zu ignorieren und den Pfad trotzdem zurückzugeben, ist es:
Der einzige Nachteil ist, dass Readlink Links folgt. Wenn Sie Links nicht folgen möchten, können Sie diese alternative Konvention verwenden:
Dadurch wird der Verzeichnisteil von $ path geändert und das aktuelle Verzeichnis zusammen mit dem Dateiteil von $ path gedruckt. Wenn chdir nicht funktioniert, erhalten Sie eine leere Zeichenfolge und einen Fehler auf stderr.
quelle
readlink
ist eine gute Option, wenn es verfügbar ist. Die OS X-Version unterstützt die Optionen-e
oder nicht-f
. In den ersten drei Beispielen sollten doppelte Anführungszeichen vorhanden sein$path
, um Leerzeichen oder Platzhalter im Dateinamen zu verarbeiten. +1 für die Parametererweiterung, dies hat jedoch eine Sicherheitslücke. Wennpath
es leer ist, wird diescd
in Ihr Home-Verzeichnis verschoben. Sie benötigen doppelte Anführungszeichen.abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Alte Frage, aber es gibt einen viel einfacheren Weg, wenn Sie mit vollständigen Pfadnamen auf Shell-Ebene arbeiten:
Da die CD in einer Subshell ausgeführt wird, hat dies keine Auswirkungen auf das Hauptskript.
Angenommen, Ihre in die Shell integrierten Befehle akzeptieren -L und -P, sind zwei Varianten:
Persönlich brauche ich diesen späteren Ansatz nur selten, wenn ich aus irgendeinem Grund von symbolischen Links fasziniert bin.
Zu Ihrer Information: Variation beim Abrufen des Startverzeichnisses eines Skripts, die auch dann funktioniert, wenn das Skript später das aktuelle Verzeichnis ändert.
Die Verwendung von CD stellt sicher, dass Sie immer das absolute Verzeichnis haben, auch wenn das Skript von Befehlen wie ./script.sh ausgeführt wird, die ohne cd / pwd oft nur ... Unnütz sind, wenn das Skript später eine CD erstellt.
quelle
Wie Adam Liss feststellte,
realpath
wird nicht mit jeder Distribution gebündelt. Was schade ist, denn es ist die beste Lösung. Der bereitgestellte Quellcode ist großartig und ich werde ihn wahrscheinlich jetzt verwenden. Folgendes habe ich bisher verwendet, das ich hier der Vollständigkeit halber teile:Wenn Sie es zu lösen Symlinks möchten, ersetzen Sie einfach
pwd
mitpwd -P
.quelle
pwd -P
Option für diesen Fall ... Überlegen Sie, was passieren würde, wenn$(basename "$1")
ein Symlink zu einer Datei in einem anderen Verzeichnis wäre. Derpwd -P
einzige löst Symlinks im Verzeichnisbereich des Pfads auf, nicht jedoch im Basisnamenbereich.Meine jüngste Lösung war:
Basierend auf der Antwort von Tim Whitcomb.
quelle
pushd $(dirname /usr/bin/java)
versuchen.Nicht gerade eine Antwort, aber vielleicht eine Folgefrage (die ursprüngliche Frage war nicht explizit):
readlink
ist in Ordnung, wenn Sie tatsächlich Symlinks folgen möchten. Es gibt aber auch einen Anwendungsfall für das bloße Normalisieren von./
und../
und//
Sequenzen, der rein syntaktisch durchgeführt werden kann, ohne Symlinks zu kanonisieren.readlink
ist nicht gut dafür und auch nichtrealpath
.funktioniert für vorhandene Pfade, bricht aber für andere.
Ein
sed
Skript scheint eine gute Wahl zu sein, außer dass Sie Sequenzen (/foo/bar/baz/../..
->/foo/bar/..
->/foo
) nicht iterativ ersetzen können, ohne etwas wie Perl zu verwenden, das nicht auf allen Systemen sicher angenommen werden kann, oder eine hässliche Schleife zu verwenden, um die Ausgabe vonsed
to zu vergleichen seine Eingabe.FWIW, ein Einzeiler mit Java (JDK 6+):
quelle
realpath
eine hat-s
Option, keine symbolischen Links zu lösen und nur Entschlossenheit Verweise auf/./
,/../
und zusätzliche entfernen/
Zeichen. In Kombination mit der-m
Option wirdrealpath
nur der Dateiname verarbeitet und keine tatsächliche Datei berührt. Es klingt nach der perfekten Lösung. Aber leiderrealpath
fehlt immer noch auf vielen Systemen...
Komponenten kann nicht syntaktisch erfolgen, wenn Symlinks beteiligt sind./one/two/../three
wie ist nicht das gleiche ,/one/three
wenntwo
ein Symlink ist/foo/bar
.Ich bin zu spät zur Party, aber dies ist die Lösung, die ich entwickelt habe, nachdem ich eine Reihe von Themen wie diese gelesen habe:
Dies wird den absoluten Pfad von $ 1 auflösen, gut mit ~ spielen, Symlinks in dem Pfad behalten, in dem sie sich befinden, und es wird nicht mit Ihrem Verzeichnisstapel in Konflikt geraten. Es gibt den vollständigen Pfad oder nichts zurück, wenn es nicht existiert. Es wird erwartet, dass 1 US-Dollar ein Verzeichnis ist, und es wird wahrscheinlich fehlschlagen, wenn dies nicht der Fall ist. Dies ist jedoch eine einfache Überprüfung, die Sie selbst durchführen können.
quelle
Gesprächige und etwas späte Antwort. Ich muss einen schreiben, da ich auf älterem RHEL4 / 5 stecke. Ich behandle absolute und relative Links und vereinfache //, /./ und somedir /../ Einträge.
quelle
Testen Sie unser neues Bash-Bibliotheksprodukt realpath-lib , das wir kostenlos und unbeschwert auf GitHub eingestellt haben. Es ist gründlich dokumentiert und ein großartiges Lernwerkzeug.
Es löst lokale, relative und absolute Pfade auf und hat keine Abhängigkeiten außer Bash 4+. es sollte also fast überall funktionieren. Es ist kostenlos, sauber, einfach und lehrreich.
Du kannst tun:
Diese Funktion ist der Kern der Bibliothek:
Es enthält auch Funktionen zu get_dirname, get_filename, get_ stemname und validate_path. Probieren Sie es plattformübergreifend aus und helfen Sie, es zu verbessern.
quelle
Das Problem dabei
realpath
ist, dass es unter BSD (oder OSX) nicht verfügbar ist. Hier ist ein einfaches Rezept, das aus einem ziemlich alten Artikel (2009) aus dem Linux Journal extrahiert wurde und ziemlich portabel ist:Beachten Sie auch diese Variante nicht nicht den Pfad erfordern zu existieren.
quelle
Basierend auf der Antwort von @ Andre könnte ich eine etwas bessere Version haben, falls jemand nach einer schleifenfreien, vollständig auf String-Manipulation basierenden Lösung sucht. Es ist auch nützlich für diejenigen, die keine Symlinks dereferenzieren möchten, was der Nachteil der Verwendung von
realpath
oder istreadlink -f
.Es funktioniert mit Bash-Versionen 3.2.25 und höher.
quelle
hello/../world
(2) Punkte im Dateinamen/hello/..world
(3) Punkte nach dem doppelten Schrägstrich/hello//../world
(4) Punkt vor oder nach dem doppelten Schrägstrich/hello//./world
oder/hello/.//world
(5) Eltern nach dem aktuellen:/hello/./../world/
(6) Eltern after Parent:/hello/../../world
usw. - Einige davon können mithilfe einer Schleife behoben werden, um Korrekturen vorzunehmen, bis sich der Pfad nicht mehr ändert. (Entfernen Sie auchdir/../
, nicht/dir/..
aber entfernen Siedir/..
vom Ende.)Basierend auf Loveborgs exzellentem Python-Snippet habe ich Folgendes geschrieben:
quelle
Dies funktioniert auch dann, wenn die Datei nicht vorhanden ist. Das Verzeichnis, in dem sich die Datei befindet, muss vorhanden sein.
quelle
realpath -e
Ich brauchte eine Lösung, die alle drei Aufgaben erfüllt:
realpath
undreadlink -f
sind AddonsKeine der Antworten hatte sowohl # 1 als auch # 2. Ich habe # 3 hinzugefügt, um anderen die weitere Yak-Rasur zu ersparen.
Hier ist ein kurzer Testfall mit einigen verdrehten Leerzeichen in den Pfaden, um das Zitat vollständig auszuführen
quelle
Ich weiß, dass dies eine alte Frage ist. Ich biete immer noch eine Alternative an. Kürzlich bin ich auf dasselbe Problem gestoßen und habe keinen vorhandenen und portablen Befehl dafür gefunden. Also habe ich das folgende Shell-Skript geschrieben, das eine Funktion enthält, die den Trick ausführen kann.
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
quelle
Ich habe eine integrierte Funktion entwickelt, um dies zu handhaben, wobei der Schwerpunkt auf der höchstmöglichen Leistung liegt (zum Spaß). Symlinks werden nicht aufgelöst, daher ist es im Grunde dasselbe wie
realpath -sm
.Hinweis: Diese Funktion verarbeitet keine Pfade, die mit beginnen
//
, da genau zwei doppelte Schrägstriche am Anfang eines Pfads ein implementierungsdefiniertes Verhalten sind. Es behandelt jedoch/
,///
und so weiter gut.Diese Funktion scheint alle Randfälle richtig zu behandeln, aber es gibt möglicherweise noch einige, mit denen ich mich nicht befasst habe.
Leistungshinweis: Wird beim Aufrufen mit Tausenden von Argumenten
abspath
etwa 10-mal langsamer ausgeführt alsrealpath -sm
; Wenn es mit einem einzelnen Argument aufgerufen wird,abspath
läuft es> 110x schneller alsrealpath -sm
auf meinem Computer, hauptsächlich weil nicht jedes Mal ein neues Programm ausgeführt werden muss.quelle
Ich habe heute festgestellt, dass Sie den
stat
Befehl zum Auflösen von Pfaden verwenden können.Also für ein Verzeichnis wie "~ / Documents":
Sie können dies ausführen:
stat -f %N ~/Documents
So erhalten Sie den vollständigen Pfad:
/Users/me/Documents
Für Symlinks können Sie die Option% Y-Format verwenden:
stat -f %Y example_symlink
Welches könnte ein Ergebnis zurückgeben wie:
/usr/local/sbin/example_symlink
Die Formatierungsoptionen können in anderen Versionen von * NIX unterschiedlich sein, aber diese haben unter OSX für mich funktioniert.
quelle
stat -f %N ~/Documents
Linie ist ein roter Hering ... Ihre Schale ersetzt~/Documents
durch/Users/me/Documents
undstat
druckt nur das Argument wörtlich.Eine einfache Lösung mit
node.js
:quelle