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.)


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.


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ć.


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
}

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

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.


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}

-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

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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.