Wie erhalte ich das absolute Verzeichnis einer Datei in Bash?


82

Ich habe ein Bash-Skript geschrieben, das eine Eingabedatei als Argument verwendet und liest.
Diese Datei enthält einige Pfade (relativ zu ihrem Speicherort) zu zusätzlichen verwendeten Dateien.

Ich möchte, dass das Skript in den Ordner mit der Eingabedatei wechselt, um weitere Befehle auszuführen.

Also, wie kann ich den Ordner (und nur den Ordner) aus einer Eingabedatei? (Unter Linux.)


Geben Sie den vollständigen Pfad zur Eingabedatei oder nur den Pfad relativ zum aktuellen Arbeitsverzeichnis an?
dmh

dhm: Eingabe gibt relativen Pfad, ich möchte absoluten Pfad minus Dateinamen.
BobMcGee

Antworten:


150

Um den vollständigen Pfad zu erhalten, verwenden Sie:

readlink -f relative/path/to/file

So rufen Sie das Verzeichnis einer Datei ab:

dirname relative/path/to/file

Sie können auch beide kombinieren:

dirname $(readlink -f relative/path/to/file)

Wenn readlink -fes auf Ihrem System nicht verfügbar ist, können Sie Folgendes verwenden * :

function myreadlink() {
  (
  cd "$(dirname $1)"         # or  cd "${1%/*}"
  echo "$PWD/$(basename $1)" # or  echo "$PWD/${1##*/}"
  )
}

Beachten Sie, dass, wenn Sie nur in ein Verzeichnis einer Datei wechseln müssen, die als relativer Pfad angegeben ist, Sie den absoluten Pfad nicht kennen müssen. Ein relativer Pfad ist vollkommen legal. Verwenden Sie also einfach:

cd $(dirname relative/path/to/file)

Wenn Sie (während das Skript ausgeführt wird) zum ursprünglichen Pfad zurückkehren möchten, verwenden Sie pushdanstelle von cdund popdwenn Sie fertig sind.


* Während das myreadlinkoben Gesagte im Zusammenhang mit dieser Frage gut genug ist, weist es einige Einschränkungen in Bezug auf das readlinkoben vorgeschlagene Tool auf. Zum Beispiel folgt es nicht richtig einem Link zu einer Datei mit einem anderen basename.


17
Beachten Sie, dass readlink -fdies unter OS X leider nicht funktioniert.
Emil Sit

Ich möchte nicht den ganzen Pfad, ich möchte nur einen Ordner. Dies bietet jedoch genug, um hilfreich zu sein: readlink -f relativeFileLocation | xargs dirname
BobMcGee

1
@EmilSit: Sie haben Recht - von Haus aus. Aber du kannst brew install coreutilses bekommen. stackoverflow.com/a/4031502/1022967 .
mpettis

3
Bitte empfehlen Sie nicht, ${1%/*}oder ${1##*/}als Ersatz für dirnameund zu verwenden basename. Die erste Unterbrechung, wenn Sie ihm einen einfachen Dateinamen geben ( dirname foo.barwäre richtig, wäre .), und die zweite Unterbrechung für ein Verzeichnis, das mit einem Schrägstrich endet ( basename /foo/bar/, wäre richtig, wäre bar).
Camilo Martin

1
@ChenLevy Manche Leute es im Interesse geneigt sein, schneller zu sein , zu verwenden (glaube ich basename/ dirnamesind nicht Einbauten), und sie werden es auf den ersten Arbeiten sehen , aber scheinbar ohne das Gute in der Zukunft irgendwann plötzlich brechen Grund. Ich würde es vermeiden, es als Option vorzuschlagen, oder ablehnen, dass es nicht wirklich ein kugelsicherer Verdächtiger ist.
Camilo Martin

19

Werfen Sie einen Blick auf die Manpage für realpath, ich benutze das und so etwas wie:

CONTAININGDIR = $ (realpath $ {FILEPATH% / *})

zu tun, wie es sich anhört, als würden Sie versuchen zu tun.


Realpath macht eine seltsame Sache mit mir, wo es Symlinks von relativen Dirs auflöst
Erik Aronesty

Vielen Dank! Arbeitete für mich unter Mac OS
Dmitry Klochkov

6

Dies funktioniert sowohl für Dateien als auch für Ordner:

absPath(){
    if [[ -d "$1" ]]; then
        cd "$1"
        echo "$(pwd -P)"
    else 
        cd "$(dirname "$1")"
        echo "$(pwd -P)/$(basename "$1")"
    fi
}

4
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

Einige Erklärungen:

  1. Dieses Skript erhält einen relativen Pfad als Argument "$1"
  2. Dann bekommen wir DIRNAME Teil dieses Weges (können Sie entweder dir oder die Datei zu diesem Skript übergeben):dirname "$1"
  3. Dann gehen wir cd "$(dirname "$1");in dieses relative Verzeichnis
  4. pwd -Pund absoluten Weg bekommen. Die -POption vermeidet Symlinks
  5. Als letzten Schritt haben wir echoes geschafft

Führen Sie dann Ihr Skript aus:

abs.sh your_file.txt

1

Probieren Sie unser neues Bash-Bibliotheksprodukt realpath-lib bei GitHub aus, das wir der Community kostenlos und unbeschwert zur Verfügung gestellt haben. Es ist sauber, einfach und gut dokumentiert, daher ist es großartig, daraus zu lernen. Du kannst tun:

get_realpath <absolute|relative|symlink|local file path>

Diese Funktion ist der Kern der Bibliothek:

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

}

Es ist Bash 4+, erfordert keine Abhängigkeiten und bietet außerdem get_dirname, get_filename, get_stemname und validate_path.


0

Das Problem mit der obigen Antwort tritt bei Dateien auf, die mit "./" wie "./my-file.txt" eingegeben wurden.

Problemumgehung (von vielen):

    myfile="./somefile.txt"
    FOLDER="$(dirname $(readlink -f "${ARG}"))"
    echo ${FOLDER}

-1

Ich habe readlink -f verwendet und funktioniert unter Linux

damit

FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD

5
Verwenden Sie in Bash keine Variablennamen in Großbuchstaben, es kommt eines Tages zu Konflikten mit bereits vorhandenen Variablen ... Oh, und dieser Tag ist jetzt: Wussten Sie, dass die Variable PWDbereits in Bash vorhanden ist und ihr Wert tatsächlich das aktuelle Verzeichnis ist? Und verwenden Sie auch mehr Anführungszeichen.
gniourf_gniourf
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.