Gibt es einen Befehl zum Abrufen des absoluten Pfads bei gegebenem relativen Pfad?
Zum Beispiel möchte ich, dass $ line den absoluten Pfad jeder Datei in dir enthält ./etc/
find ./ -type f | while read line; do
echo $line
done
Gibt es einen Befehl zum Abrufen des absoluten Pfads bei gegebenem relativen Pfad?
Zum Beispiel möchte ich, dass $ line den absoluten Pfad jeder Datei in dir enthält ./etc/
find ./ -type f | while read line; do
echo $line
done
Antworten:
verwenden:
find "$(pwd)"/ -type f
um alle Dateien zu bekommen oder
echo "$(pwd)/$line"
um den vollständigen Pfad anzuzeigen (wenn der relative Pfad wichtig ist)
$(pwd)
anstelle von verwenden $PWD
? (Ja, ich weiß, pwd
ist ein eingebautes)
Versuchen Sie es realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Verwenden Sie, um das Erweitern von Symlinks zu vermeiden realpath -s
.
Die Antwort kommt von " Befehl bash / fish, um den absoluten Pfad zu einer Datei zu drucken ".
realpath
scheint auf dem Mac nicht verfügbar zu sein (OS X 10.11 "El Capitan"). :-(
realpath
scheint auch unter CentOS 6 nicht verfügbar zu sein
brew install coreutils
wird inrealpath
realpath
ist schon vorhanden. Ich musste es nicht separat installieren.
realpath
ist es auf Git für Windows verfügbar (zumindest für mich).
Wenn Sie das Paket coreutils installiert haben, können Sie es im Allgemeinen verwenden, readlink -f relative_file_name
um das absolute Paket abzurufen (wobei alle Symlinks aufgelöst sind).
brew install coreutils
. Die ausführbare Datei wird jedoch von ag vorangestellt:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Einige Erklärungen
"$1"
dirname "$1"
cd "$(dirname "$1")
in dieses relative Verzeichnis und erhalten den absoluten Pfad dafür, indem wir den pwd
Shell-Befehl ausführen$(basename "$1")
echo
es geschafftrealpath
oder was zu readlink -f
tun ist. Beispielsweise funktioniert es nicht auf Pfaden, bei denen die letzte Komponente ein Symlink ist.
-P
Option zum pwd
Befehl bereitstellen :echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Für das, was es wert ist, habe ich für die Antwort gestimmt, die ausgewählt wurde, wollte aber eine Lösung teilen. Der Nachteil ist, dass es nur Linux ist - ich habe ungefähr 5 Minuten lang versucht, das OSX-Äquivalent zu finden, bevor ich zum Stack-Überlauf kam. Ich bin mir aber sicher, dass es da draußen ist.
Unter Linux können Sie readlink -e
zusammen mit verwenden dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
ergibt
/etc/
Und dann benutzt du die dirname
Schwester, basename
um nur den Dateinamen zu bekommen
$(basename ../../../../../passwd)
ergibt
passwd
Alles zusammen ..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
ergibt
/etc/passwd
Sie sind sicher, wenn Sie auf ein Verzeichnis abzielen, basename
nichts zurückgeben und in der endgültigen Ausgabe nur doppelte Schrägstriche erhalten.
dirname
, readlink
und basename
. Das hat mir geholfen, den absoluten Weg einer symbolischen Verbindung zu finden - nicht ihr Ziel.
/home/GKFX
und ich gebe touch newfile
, dann , bevor ich die Eingabetaste drücken könnten Sie arbeiten heraus , dass ich meine „create / home / GKFX / newfile“, die ein absoluter Pfad zu einer Datei, die noch nicht existiert.
Ich denke, das ist das tragbarste:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Es schlägt jedoch fehl, wenn der Pfad nicht vorhanden ist.
dirname
ist ein GNU-Kerndienstprogramm, das meiner Meinung nach nicht allen Unixen gemeinsam ist.
dirname
ist ein POSIX-Standarddienstprogramm, siehe hier: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
seit einem Tag verwendet wird, und jetzt, wo ich diesen Papierkorb durch basename "$1"
ihn ersetzt habe, scheint es endlich richtig zu sein, Pfade zu behandeln, die mit / enden.
../..
realpath
ist wahrscheinlich am besten
Aber ...
Die anfängliche Frage war anfangs sehr verwirrt, mit einem Beispiel, das, wie angegeben, schlecht mit der Frage zusammenhängt.
Die ausgewählte Antwort beantwortet tatsächlich das gegebene Beispiel und überhaupt nicht die Frage im Titel. Der erste Befehl ist diese Antwort (ist es wirklich? Ich bezweifle) und könnte auch ohne das '/' auskommen. Und ich sehe nicht, was der zweite Befehl tut.
Mehrere Themen sind gemischt:
Ändern eines relativen Pfadnamens in einen absoluten, was auch immer es bedeutet, möglicherweise nichts. (In der Regel, wenn Sie einen Befehl wie ztouch foo/bar
, foo/bar
muss der Pfadname für Sie vorhanden sein und möglicherweise für die Berechnung verwendet werden, bevor die Datei tatsächlich erstellt wird. )
Es kann mehrere absolute Pfadnamen geben, die dieselbe Datei (oder potenzielle Datei) bezeichnen, insbesondere aufgrund symbolischer Links (Symlinks) auf dem Pfad, möglicherweise jedoch aus anderen Gründen (ein Gerät kann doppelt als schreibgeschützt bereitgestellt werden). Man kann solche Symlinks explizit auflösen oder nicht.
Erreichen des Endes einer Kette symbolischer Links zu einer Datei oder einem Namen ohne Symlink. Dies kann einen absoluten Pfadnamen ergeben oder nicht, abhängig davon, wie es gemacht wird. Und man kann es in einen absoluten Pfadnamen auflösen oder nicht.
Der Befehl readlink foo
ohne Option gibt nur dann eine Antwort, wenn sein Argument foo
ein symbolischer Link ist und diese Antwort der Wert dieses Symlinks ist. Es wird kein anderer Link verfolgt. Die Antwort kann ein relativer Pfad sein: Was auch immer der Wert des Symlink-Arguments war.
Jedoch, readlink
Verfügt über Optionen (-f -e oder -m), die für alle Dateien funktionieren, und gibt der Datei, die tatsächlich durch das Argument gekennzeichnet ist, einen absoluten Pfadnamen (den ohne Symlinks).
Dies funktioniert gut für alles, was kein Symlink ist, obwohl man möglicherweise einen absoluten Pfadnamen verwenden möchte, ohne die Zwischensymlinks auf dem Pfad aufzulösen. Dies erfolgt durch den Befehlrealpath -s foo
Im Fall eines Symlink-Arguments werden readlink
mit seinen Optionen wieder alle Symlinks auf dem absoluten Pfad zum Argument aufgelöst, aber dies schließt auch alle Symlinks ein, die auftreten können, wenn Sie dem Argumentwert folgen. Möglicherweise möchten Sie dies nicht, wenn Sie einen absoluten Pfad zum Argument symlink selbst wünschen, und nicht zu dem, mit dem es verknüpft ist. Auch wenn if foo
ein Symlink ist,realpath -s foo
wird ein absoluter Pfad ohne Auflösen von Symlinks erhalten, einschließlich des als Argument angegebenen.
Ohne die -s
Option realpath
funktioniert dies ziemlich genau so
readlink
, außer dass nur der Wert eines Links und einige andere Dinge gelesen werden. Mir ist einfach nicht klar, warum readlink
es seine Optionen gibt, was anscheinend zu einer unerwünschten Redundanz führt
realpath
.
Das Erkunden des Webs sagt nicht viel mehr aus, außer dass es einige Unterschiede zwischen den Systemen geben kann.
Fazit: realpath
ist der beste Befehl mit der größten Flexibilität, zumindest für die hier angeforderte Verwendung.
Meine Lieblingslösung war die von @EugenKonkov, da dies nicht die Anwesenheit anderer Dienstprogramme (das Coreutils-Paket) implizierte.
Aber es ist für die relativen Pfade "fehlgeschlagen." und "..", daher hier eine leicht verbesserte Version, die diese Sonderfälle behandelt.
Es schlägt jedoch immer noch fehl, wenn der Benutzer nicht über die Berechtigung verfügt, cd
in das übergeordnete Verzeichnis des relativen Pfads zu wechseln.
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
Eugens Antwort hat bei mir nicht ganz funktioniert, aber das hat funktioniert:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Nebenbei bemerkt, Ihr aktuelles Arbeitsverzeichnis ist nicht betroffen.
Die beste Lösung, imho, ist die hier veröffentlichte: https://stackoverflow.com/a/3373298/9724628 .
Es erfordert Python, um zu funktionieren, aber es scheint alle oder die meisten Randfälle abzudecken und eine sehr tragbare Lösung zu sein.
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
Wenn Sie Bash unter Mac OS X verwenden, bei dem weder ein Realpath vorhanden ist noch der Readlink den absoluten Pfad drucken kann, haben Sie möglicherweise die Wahl, Ihre eigene Version zu codieren, um ihn zu drucken. Hier ist meine Implementierung:
(reine Bash)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(benutze gawk)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(pure bsd awk)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
Beispiel:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Wenn der relative Pfad ein Verzeichnispfad ist, versuchen Sie meinen, sollte der beste sein:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
Eine Verbesserung der ziemlich schönen Version von @ ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Dies behandelt den Fall korrekt, in dem das letzte Element des Pfades ist ..
. In diesem Fall wird die "$(pwd)/$(basename "$1")"
Antwort von in @ ernest-a als durchkommen accurate_sub_path/spurious_subdirectory/..
.
Wenn Sie eine Variable mit einem relativen Pfad in einen absoluten Pfad umwandeln möchten, funktioniert dies wie folgt:
dir=`cd "$dir"`
"cd" hallt wider, ohne das Arbeitsverzeichnis zu ändern, da es hier in einer Sub-Shell ausgeführt wird.
dir=`cd ".."` && echo $dir
Dies ist eine verkettete Lösung von allen anderen, z. B. wenn ein realpath
Fehler auftritt, entweder weil sie nicht installiert ist oder weil sie mit Fehlercode beendet wird, wird die nächste Lösung versucht, bis der richtige Pfad gefunden wurde.
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
Sie können die Bash-String-Ersetzung für jeden relativen Pfad $ line verwenden:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
Die grundlegende Front-of-String-Ersetzung folgt der Formel
$ {string / # substring / replace},
die hier ausführlich erläutert wird: https://www.tldp.org/LDP/abs/html/string-manipulation.html
Das \
Zeichen negiert das, /
wenn es Teil der Zeichenfolge sein soll, die wir finden / ersetzen.
Ich konnte keine Lösung finden, die zwischen Mac OS Catalina, Ubuntu 16 und Centos 7 problemlos portierbar war. Daher habe ich mich für Python Inline entschieden und es funktionierte gut für meine Bash-Skripte.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"