Jak uzyskać katalog bezwzględny pliku w bash?

82

Napisałem skrypt bash, który pobiera plik wejściowy jako argument i czyta go.
Ten plik zawiera ścieżki (w stosunku do jego lokalizacji) do dodatkowych używanych plików.

Chciałbym, aby skrypt przeszedł do folderu zawierającego plik wejściowy, aby wykonać dalsze polecenia.

Jak więc uzyskać folder (i tylko folder) z pliku wejściowego? (W systemie Linux.)

BobMcGee
źródło
Czy podajesz pełną ścieżkę do pliku wejściowego, czy tylko ścieżkę względną do bieżącego katalogu roboczego?
dmh
dhm: wejście daje ścieżkę względną, chcę ścieżkę bezwzględną minus nazwa pliku.
BobMcGee,

Odpowiedzi:

152

Aby uzyskać pełną ścieżkę, użyj:

readlink -f relative/path/to/file

Aby uzyskać katalog pliku:

dirname relative/path/to/file

Możesz również połączyć te dwa elementy:

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

Jeśli readlink -fnie jest dostępny w Twoim systemie, możesz użyć tego * :

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

Zauważ, że jeśli potrzebujesz tylko przenieść się do katalogu pliku określonego jako ścieżka względna, nie musisz znać ścieżki bezwzględnej, ścieżka względna jest całkowicie legalna, więc po prostu użyj:

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

jeśli chcesz wrócić (podczas działania skryptu) do oryginalnej ścieżki, użyj pushdzamiast cd, i popdkiedy skończysz.


* Chociaż myreadlinkpowyższe jest wystarczająco dobre w kontekście tego pytania, ma pewne ograniczenia w stosunku do readlinknarzędzia sugerowanego powyżej. Na przykład nie podąża poprawnie za łączem do pliku z innym rozszerzeniem basename.

Chen Levy
źródło
17
Zauważ, że readlink -fniestety nie działa na OS X.
Emil Sit
Nie chcę całej ścieżki, chcę tylko folderu. Ale to wystarcza, by być pomocnym: readlink -f względnaLokalizacjaPliku | xargs dirname
BobMcGee,
1
@EmilSit: masz rację - natywnie. Ale możesz brew install coreutilsto zdobyć. stackoverflow.com/a/4031502/1022967 .
mpettis
3
Nie zalecamy używania ${1%/*}ani ${1##*/}jako substytutów dirnamei basename. Pierwszy przerywa, jeśli nadasz mu prostą nazwę pliku ( dirname foo.barbyłby, poprawnie, be .), a drugi przerywa katalog kończący się ukośnikiem ( basename /foo/bar/byłby, poprawnie, byłby bar).
Camilo Martin,
1
@ChenLevy Niektórzy ludzie mogą być skłonni do używania go ze względu na szybkość (myślę basename/dirname nie są wbudowane) i zobaczą, że działa na początku, ale nagle w pewnym momencie w przyszłości zepsuje się, bo pozornie nie jest dobre powód. Unikałbym sugerowania tego jako opcji lub stwierdzenia, że ​​tak naprawdę nie jest to substytut kuloodporny.
Camilo Martin,
19

Spójrz na stronę podręcznika realpath, używam tego i czegoś takiego:

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

robić to, co wygląda na to, że próbujesz zrobić.

Richard Sitze
źródło
realpath robi dla mnie dziwne, gdy rozwiązuje dowiązania symboliczne do względnych
reżerów
Dzięki! Pracował dla mnie na Mac OS
Dmitry Klochkov
6

To zadziała zarówno dla pliku, jak i folderu:

absPath(){
    if [[ -d "$1" ]]; then
        cd "$1"
        echo "$(pwd -P)"
    else 
        cd "$(dirname "$1")"
        echo "$(pwd -P)/$(basename "$1")"
    fi
}
Jahid
źródło
4
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

Kilka wyjaśnień:

  1. Ten skrypt pobiera ścieżkę względną jako argument "$1"
  2. Następnie otrzymujemy część dirname tej ścieżki (możesz przekazać katalog lub plik do tego skryptu):dirname "$1"
  3. Następnie cd "$(dirname "$1");przechodzimy do tego względnego reż
  4. pwd -Pi uzyskaj absolutną ścieżkę. Ta -Popcja pozwoli uniknąć dowiązań symbolicznych
  5. Jako ostatni krok zrobiliśmy echoto

Następnie uruchom skrypt:

abs.sh your_file.txt
Eugen Konkov
źródło
1

Wypróbuj naszą nową bibliotekę Bash realpath-lib na GitHub, którą udostępniliśmy społeczności do bezpłatnego i nieobciążonego użytku. Jest przejrzysty, prosty i dobrze udokumentowany, więc warto się z niego uczyć. Możesz to zrobić:

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

Ta funkcja jest rdzeniem biblioteki:

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

}

Jest to Bash 4+, nie wymaga żadnych zależności, a także zapewnia get_dirname, get_filename, get_stemname i validate_path.

AsymLabs
źródło
0

Problem z powyższą odpowiedzią dotyczy plików wprowadzanych z „./” jak „./my-file.txt”

Obejście (z wielu):

    myfile="./somefile.txt"
    FOLDER="$(dirname $(readlink -f "${ARG}"))"
    echo ${FOLDER}
Mike Q
źródło
-1

Używam readlink -f działa na Linuksie

więc

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

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD
Mehul Rathod
źródło
5
nie używaj nazw zmiennych pisanych wielkimi literami w Bash, któregoś dnia zderzą się z już istniejącymi zmiennymi ... och, a ten dzień jest teraz: czy wiesz, że zmienna PWDjuż istnieje w Bash, a jej wartość jest w rzeczywistości bieżącym katalogiem? I użyj więcej cytatów.
gniourf_gniourf,