it-swarm.com.de

Bash: Absolute Pfadangabe relativ

Gibt es einen Befehl, um den absoluten Pfad anhand des relativen Pfads abzurufen?

Zum Beispiel möchte ich, dass $ line den absoluten Pfad jeder Datei in dir ./etc/ enthält.

find ./ -type f | while read line; do
   echo $line
done
102
nubme

benutzen:

find $(pwd)/ -type f

um alle Dateien zu erhalten oder

echo $(pwd)/$line

vollständigen Pfad anzeigen (wenn relativer Pfad wichtig ist)

44
mpapis

Versuchen Sie realpath

~ $ Sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

Um das Erweitern von Symlinks zu vermeiden, verwenden Sie realpath -s.

Die Antwort kommt vom Befehl " bash/fish", um den absoluten Pfad zu einer Datei zu drucken ".

111
epere4

Wenn Sie das coreutils-Paket installiert haben, können Sie im Allgemeinen readlink -f relative_file_name verwenden, um das absolute Paket abzurufen (wobei alle Symlinks aufgelöst wurden).

90
Moritz Bunkus
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPDEinige Erklärungen

  1. Dieses Skript erhält einen relativen Pfad als Argument "$1".
  2. Dann erhalten wir dirname Teil dieses Pfads (Sie können entweder dir oder eine Datei an dieses Skript übergeben): dirname "$1"
  3. Dann cd "$(dirname "$1") in dieses relative Verzeichnis und erhalten den absoluten Pfad durch Ausführen des pwd Shell-Befehls
  4. Danach hängen wir basename an den absoluten Pfad an: $(basename "$1")
  5. Als letzten Schritt echo it
40
Eugen Konkov

Für das, was es wert ist, habe ich für die gewählte Antwort gestimmt, wollte aber eine Lösung teilen. Der Nachteil ist, dass es nur Linux ist. Ich habe ungefähr fünf Minuten damit verbracht, das OSX-Äquivalent zu finden, bevor ich zum Stack-Überlauf kam. Ich bin mir sicher, dass es da draußen ist.

Unter Linux können Sie readlink -e in Kombination mit dirname verwenden.

$(dirname $(readlink -e ../../../../etc/passwd))

erträge

/etc/

Und dann benutzen Sie dirnames Schwester, basename, um einfach den Dateinamen Zu erhalten

$(basename ../../../../../passwd)

erträge 

passwd

Alles zusammen ..

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

erträge

/etc/passwd

Sie sind sicher, wenn Sie ein Verzeichnis als Ziel festlegen, basename gibt nichts zurück Sie erhalten schließlich in der endgültigen Ausgabe doppelte Schrägstriche.

29

Ich denke, das ist das tragbarste:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

Wenn der Pfad nicht vorhanden ist, schlägt dies fehl.

21
Ernest A

realpath ist wahrscheinlich am besten

Aber ...

Die anfängliche Frage war zunächst sehr verwirrt, wobei sich ein Beispiel schlecht auf die Frage bezog.

Die ausgewählte Antwort antwortet tatsächlich auf das gegebene Beispiel und überhaupt nicht auf die Frage im Titel. Der erste Befehl ist diese Antwort (ist es wirklich ... wirklich? Ich bezweifle ich) und könnte auch ohne das '/' auskommen. Und ich kann nicht sehen, was der zweite Befehl gerade macht.

Einige Probleme sind gemischt:

  • Ändern eines relativen Pfadnamens in einen absoluten, was auch immer bedeutet, möglicherweise nichts . ( Wenn Sie einen Befehl wie touch foo/bar ausgeben, muss der Pfadname foo/bar normalerweise für Sie vorhanden sein und möglicherweise sein Wird in Berechnung verwendet, bevor die Datei erstellt wird. )

  • es kann mehrere absolute Pfadnamen geben, die dieselbe Datei (oder potenzielle Datei) angeben, insbesondere aufgrund symbolischer Links (Symlinks) im Pfad, möglicherweise jedoch aus anderen Gründen (ein Gerät kann zweimal angehängt werden.) als schreibgeschützt). Man kann oder möchte nicht Diese Symlinks lösen.

  • das Ende einer Kette symbolischer Links zu einer nicht-symbolischen .__-Datei oder einem Namen. Je nachdem, wie er ausgeführt wird, kann dies einen absoluten Pfadnamen ergeben. Und man kann oder will es nicht In einen absoluten Pfadnamen auflösen.

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. Kein anderer Link wird verfolgt. Die Antwort kann ein relativer Pfad sein: .__ was auch immer der Wert des Symlink-Arguments war.

readlink hat jedoch Optionen (-f -e oder -m), die für alle -Dateien geeignet sind und einen absoluten Pfadnamen (den ohne Symlinks) an die -Datei geben, die tatsächlich durch das Argument angegeben wird.

Dies funktioniert gut für alles, was kein Symlink ist, obwohl Einen absoluten Pfadnamen verwenden möchte, ohne die intermediären -Symlinks im Pfad aufzulösen. Dies geschieht mit dem Befehl realpath -s foo

Im Fall eines Symlink-Arguments löst readlink mit seinen Optionen Erneut alle Symlinks auf dem absoluten Pfad des Arguments auf, aber Das schließt auch alle Symlinks ein, auf die Nach dem Argumentwert stoßen kann . Möglicherweise möchten Sie dies nicht, wenn Sie einen Absoluten Pfad zum Argument symlink selbst wünschen, und nicht zu was auch immerit kann verlinkt werden. Wenn foo ein symbolischer Link ist, erhält realpath -s foo einen absoluten Pfad ohne Auflösung von symbolischen Links, einschließlich des als Argument angegebenen .

Ohne die -s-Option macht realpath praktisch das Gleiche wie _____readlink, es sei denn, Sie lesen einfach den Wert eines Links sowie einige andere Dinge. Mir ist nur unklar, warum readlink seine -Optionen hat und scheinbar mit realpath eine unerwünschte Redundanz erzeugt.

Das Erkunden des Webs sagt nicht viel mehr aus, außer dass es einige Unterschiede zwischen den Systemen gibt.

Schlussfolgerung: realpath ist der beste Befehl für die Verwendung mit der __ größten Flexibilität, zumindest für die hier angeforderte Verwendung.

13
babou

Eugens Antwort hat für mich nicht ganz funktioniert, aber dies tat:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

Anmerkung: Ihr aktuelles Arbeitsverzeichnis ist nicht betroffen.

5
biomiker

Meine Lieblingslösung war the one von @EugenKonkov, da dies nicht die Anwesenheit anderer Dienstprogramme (das coreutils-Paket) implizierte.

Aber für die relativen Pfade "." und "..", hier ist eine etwas verbesserte Version, die diese Sonderfälle behandelt.

Es schlägt jedoch weiterhin fehl, wenn der Benutzer nicht über die Berechtigung für cd im übergeordneten Verzeichnis des relativen Pfads verfügt.

#! /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
}
3
hashchange

Wenn Sie bash unter Mac OS X verwenden, das weder realpath existiert noch dessen readlink den absoluten Pfad drucken kann, haben Sie möglicherweise die Möglichkeit, Ihre eigene Version zu codieren, um sie zu drucken. __ Hier ist meine Implementierung:

(pure 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
2
Meow

Im Fall von find ist es wahrscheinlich am einfachsten, den absoluten Pfad für die Suche anzugeben, z.

find /etc
find `pwd`/subdir_of_current_dir/ -type f
2
Arkku

Dies ist eine verkettete Lösung von allen anderen. Wenn beispielsweise realpath fehlschlägt, weil es nicht installiert ist oder weil es mit Fehlercode beendet wird, wird die nächste Lösung versucht, bis der Pfad richtig ist.

#!/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")" "${?}"
0
user
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
0
Josh Barton

Ähnlich wie die Antwort von @ ernest-a, aber ohne $OLDPWD zu beeinflussen oder eine neue Funktion zu definieren, könnten Sie eine Subshell (cd <path>; pwd) auslösen.

$ pwd
/etc/Apache2
$ cd ../cups 
$ cd -
/etc/Apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
0
fakedrake

Die beste Lösung, imho, ist die hier veröffentlichte: https://stackoverflow.com/a/3373298/9724628 .

Damit Python funktioniert, muss es alle oder die meisten Edge-Fälle abdecken und eine sehr tragbare Lösung sein.

  1. Mit auflösenden Symlinks:
python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
  1. oder ohne es:
python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file
0
MrSegFaulty

Wenn der relative Pfad ein Verzeichnispfad ist, versuchen Sie es mit meinem.

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath
0
Shunfang Lan

Wenn Sie eine Variable, die einen relativen Pfad enthält, in einen absoluten umwandeln möchten, funktioniert dies:

   dir=`cd "$dir"`

"cd" hallt aus, ohne das Arbeitsverzeichnis zu ändern, da es hier in einer Sub-Shell ausgeführt wird.

0
Eric H.

Was sie sagten, außer find $PWD oder (in bash) find ~+ ist etwas bequemer.

0
Tobu