Làm cách nào để lấy thư mục tuyệt đối của tệp trong bash?


81

Tôi đã viết một tập lệnh bash lấy tệp đầu vào làm đối số và đọc nó.
Tệp này chứa một số đường dẫn (liên quan đến vị trí của nó) đến các tệp bổ sung được sử dụng.

Tôi muốn tập lệnh chuyển đến thư mục chứa tệp đầu vào, để thực hiện các lệnh khác.

Vì vậy, làm cách nào để lấy thư mục (và chỉ thư mục) từ tệp đầu vào? (Trong linux.)


Bạn đang cung cấp đường dẫn đầy đủ đến tệp đầu vào hay chỉ là đường dẫn liên quan đến thư mục làm việc hiện tại?
dmh

dhm: đầu vào cung cấp đường dẫn tương đối, tôi muốn đường dẫn tuyệt đối trừ tên tệp.
BobMcGee

Câu trả lời:


148

Để sử dụng đường dẫn đầy đủ:

readlink -f relative/path/to/file

Để lấy thư mục của tệp:

dirname relative/path/to/file

Bạn cũng có thể kết hợp cả hai:

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

Nếu readlink -fkhông có sẵn trên hệ thống của bạn, bạn có thể sử dụng cái này * :

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

Lưu ý rằng nếu bạn chỉ cần di chuyển đến thư mục của tệp được chỉ định là đường dẫn tương đối, bạn không cần biết đường dẫn tuyệt đối, đường dẫn tương đối là hoàn toàn hợp pháp, vì vậy chỉ cần sử dụng:

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

nếu bạn muốn quay lại (trong khi tập lệnh đang chạy) về đường dẫn ban đầu, hãy sử dụng pushdthay vì cdpopdkhi bạn hoàn tất.


* Mặc dù myreadlinkở trên là đủ tốt trong bối cảnh của câu hỏi này, nhưng nó có một số hạn chế so với readlinkcông cụ được đề xuất ở trên. Ví dụ: nó không theo đúng một liên kết đến một tệp khác basename.


17
Xin lưu ý rằng điều readlink -fđó không hoạt động trên OS X, rất tiếc.
Emil Sit

Tôi không muốn toàn bộ đường dẫn, tôi chỉ muốn thư mục. Nhưng điều này cung cấp đủ để hữu ích: readlink -f relativeFileLocation | dirname xargs
BobMcGee

1
@EmilSit: bạn đúng - bẩm sinh. Nhưng bạn có thể brew install coreutilslấy nó. stackoverflow.com/a/4031502/1022967 .
mpettis

3
Vui lòng không khuyến nghị sử dụng ${1%/*}hoặc ${1##*/}thay thế cho dirnamebasename. Dấu ngắt đầu tiên nếu bạn đặt cho nó một tên tệp đơn giản ( dirname foo.barwould, đúng, là .) và ngắt thứ hai cho thư mục kết thúc bằng dấu gạch chéo ( basename /foo/bar/, would, đúng, be bar).
Camilo Martin

1
@ChenLevy Một số người có thể có xu hướng sử dụng nó vì lợi ích nhanh hơn (tôi nghĩ là basename/ dirnamekhông được tích hợp sẵn) và họ sẽ thấy nó hoạt động lúc đầu nhưng đột nhiên bị hỏng vào một thời điểm nào đó trong tương lai dường như không tốt lý do. Tôi sẽ tránh đề xuất nó như một lựa chọn hoặc từ chối rằng nó không thực sự là một chất chống đạn.
Camilo Martin

18

Hãy xem trang người đàn ông realpath, tôi sử dụng trang đó và những thứ như:

CONTAININGDIR = $ (đường dẫn thực $ {FILEPATH% / *})

để làm những gì có vẻ như bạn đang cố gắng làm.


realpath làm một lẻ với tôi, nơi nó giải quyết liên kết tượng trưng của dirs tương đối
Erik Aronesty

Cảm ơn! Làm việc cho tôi trên Mac OS
Dmitry Klochkov

6

Điều này sẽ hoạt động cho cả tệp và thư mục:

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

Một số giải thích:

  1. Tập lệnh này nhận đường dẫn tương đối làm đối số "$1"
  2. Sau đó, chúng tôi nhận được một phần tên dirname của đường dẫn đó (bạn có thể chuyển dir hoặc tệp vào tập lệnh này):dirname "$1"
  3. Sau đó, chúng tôi cd "$(dirname "$1");vào dir tương đối này
  4. pwd -Pvà nhận được đường dẫn tuyệt đối. Các -Ptùy chọn sẽ tránh symlink
  5. Như bước cuối cùng chúng ta echo

Sau đó chạy tập lệnh của bạn:

abs.sh your_file.txt

1

Hãy thử sản phẩm thư viện Bash mới của chúng tôi realpath-lib tại GitHub mà chúng tôi đã cung cấp cho cộng đồng để sử dụng miễn phí và không bị cản trở. Nó rõ ràng, đơn giản và được ghi chép đầy đủ nên thật tuyệt vời để học hỏi. Bạn có thể làm:

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

Chức năng này là cốt lõi của thư viện:

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

}

Đó là Bash 4+, không yêu cầu bất kỳ phụ thuộc nào và cũng cung cấp get_dirname, get_filename, get_stemname và validate_path.


0

Vấn đề với câu trả lời ở trên xảy ra với các tệp nhập bằng "./" như "./my-file.txt"

Cách giải quyết (trong số nhiều):

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

-1

Tôi đã sử dụng readlink -f works trên linux

vì thế

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

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD

5
không sử dụng tên biến viết hoa trong Bash, một ngày nào đó nó sẽ đụng độ với các biến đã có sẵn ... oh, và ngày hôm nay là: bạn có biết biến PWDđã tồn tại trong Bash một giá trị của nó thực sự là thư mục hiện tại không? Và sử dụng nhiều dấu ngoặc kép nữa.
gniourf_gniourf
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.