Mở rộng một đường dẫn tương đối có thể có trong bash


82

Như các đối số cho tập lệnh của tôi, có một số đường dẫn tệp. Tất nhiên, chúng có thể là tương đối (hoặc chứa ~). Nhưng đối với các hàm tôi đã viết, tôi cần các đường dẫn tuyệt đối, nhưng không giải quyết được các liên kết tượng trưng của chúng.

Có bất kỳ chức năng cho điều này?



7
readlinkgiải quyết các liên kết tượng trưng; OP không muốn điều đó.
Keith Thompson



1
echo $(cd some_directory && pwd), không giải quyết được liên kết biểu tượng, không thành công nếu some_directory không tồn tại. thư mục làm việc không bị ảnh hưởng. hoặc gán cho một biến MY_PATH=$(cd some_directory && pwd).
qeatzy

Câu trả lời:


79

MY_PATH=$(readlink -f $YOUR_ARG)sẽ giải quyết các đường dẫn tương đối như "./""../"

Cũng nên xem xét điều này ( nguồn ):

#!/bin/bash
dir_resolve()
{
cd "$1" 2>/dev/null || return $?  # cd to desired directory; if fail, quell any error messages but return exit status
echo "`pwd -P`" # output full, link-resolved path
}

# sample usage
if abs_path="`dir_resolve \"$1\"`"
then
echo "$1 resolves to $abs_path"
echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected
else
echo "Could not reach $1"
fi

điều đó chắc chắn là tốt, nhưng vấn đề là các liên kết sim đã được giải quyết, và điều đó sẽ dẫn đến một số nhầm lẫn với người dùng.
laar

man pwd: "-P Hiển thị thư mục làm việc hiện tại vật lý (tất cả các liên kết tượng trưng đã được giải quyết)", chỉ cần xóa-P
andsens

7
Để có được readlink -fchức năng trên OS X, bạn có thể sử dụng Homebrew rồi chạy: $ brew install coreutils Sau đó, bạn có thể sử dụng greadlink -f.
jgpawletko

45

http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.html có những điều sau

function abspath {
    if [[ -d "$1" ]]
    then
        pushd "$1" >/dev/null
        pwd
        popd >/dev/null
    elif [[ -e "$1" ]]
    then
        pushd "$(dirname "$1")" >/dev/null
        echo "$(pwd)/$(basename "$1")"
        popd >/dev/null
    else
        echo "$1" does not exist! >&2
        return 127
    fi
}

sử dụng pushd/ popdđể chuyển sang trạng thái pwdhữu ích.


2
Đáng buồn thay, đây thực sự là câu trả lời tốt nhất ở đây và nó không được bình chọn cho đến nơi nó thuộc về. +1 để trả lời chính xác câu hỏi của OP. Anh ấy muốn các liên kết tượng trưng không được giải quyết, đó thực sự là một câu hỏi về địa phương của bash DIRSTACKvà không có gì khác. Giải pháp tốt!
cptstubing06,

5
Khi hướng đến sự shtương thích tiêu chuẩn , pushd; cd <dir>; <cmd>; popdcó thể được thay thế bằng (cd <dir>; <cmd>).
Abbafei

16

Một lớp lót đơn giản:

function abs_path {
  (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}")
}

Sử dụng:

function do_something {
    local file=$(abs_path $1)
    printf "Absolute path to %s: %s\n" "$1" "$file"
}
do_something $HOME/path/to/some\ where

Tôi vẫn đang cố gắng tìm ra cách tôi có thể làm cho nó hoàn toàn không biết liệu đường dẫn có tồn tại hay không (vì vậy nó cũng có thể được sử dụng khi tạo tệp).


1
Điều này sẽ không thay đổi thư mục làm việc hiện tại? cd -Sau đó tôi có nên đặt lại nó không?
defos1

4
Không cần. Lưu ý các parens, chúng làm cho các lệnh bên trong được chạy trong một vỏ con. Trạng thái (ví dụ như thư mục làm việc) của một vỏ con không bị rò rỉ đến vỏ mẹ của nó. Đọc thêm tại đây: tldp.org/LDP/abs/html/subshells.html
andsens

1
Một lớp lót đẹp. Tuy nhiên, có một vấn đề: nếu giá trị mở rộng của $ 1 không có bất kỳ dấu gạch chéo nào (tức là nó là tên tệp trong thư mục hiện tại), thì $ {1% / *} sẽ tự mở rộng thành tên tệp và lệnh cd không thành công. Bạn có thể muốn sử dụng $ (dirname $ 1) để thay thế, sẽ mở rộng thành '.' trong trường hợp đó.
Paulo

Bạn nói đúng, đã cập nhật. Tôi gặp một số vấn đề về trích dẫn với việc thay thế ${1##*/}bằng basenamemặc dù mà không đặt nó vào một biến riêng biệt, các đường dẫn có dấu cách không hoạt động bình thường.
andsens

1
@BryanP Tôi thực sự đã kết thúc hoàn toàn quá tải cho trình đồng bộ hóa dotfile mà tôi đang duy trì. Nó có một hàm clean_path () loại bỏ các phần đường dẫn không cần thiết, nếu bạn dán một đường dẫn tương đối vào cuối $PWDvà chạy nó qua đó, nó sẽ hoạt động khá tốt: github.com/andsens/homeshick/blob/…
andsens

8

Đây là mẹo cho tôi trên OS X: $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)

Nó sẽ hoạt động ở bất cứ đâu. Các giải pháp khác dường như quá phức tạp.


4

trên OS X bạn có thể sử dụng

stat -f "%N" YOUR_PATH

trên linux, bạn có thể có realpathtệp thực thi. nếu không, những điều sau có thể hoạt động (không chỉ cho các liên kết):

readlink -c YOUR_PATH

20
Câu trả lời OS X không hoạt động. stat -f "%N" PATHcho tôi chính xác con đường tôi đã cho nó.
Lily Ballard,

3

Sử dụng readlink -f <relative-path>, ví dụ:

export FULLPATH=`readlink -f ./`

2

Có thể điều này dễ đọc hơn và không sử dụng vỏ con và không thay đổi dir hiện tại:

dir_resolve() {
  local dir=`dirname "$1"`
  local file=`basename "$1"`
  pushd "$dir" &>/dev/null || return $? # On error, return error code
  echo "`pwd -P`/$file" # output full, link-resolved path with filename
  popd &> /dev/null
}

1

Có một phương pháp khác. Bạn có thể sử dụng phương pháp nhúng python trong tập lệnh bash để giải quyết một đường dẫn tương đối.

abs_path=$(python3 - <<END
from pathlib import Path
path = str(Path("$1").expanduser().resolve())
print(path)
END
)

0

tự chỉnh sửa, tôi chỉ nhận thấy OP nói rằng anh ấy không tìm kiếm các liên kết tượng trưng được giải quyết:

"Nhưng đối với các chức năng tôi đã viết, tôi cần các đường dẫn tuyệt đối, nhưng không giải quyết các liên kết tượng trưng của chúng."

Vì vậy, đoán rằng đây không phải là điều quá khó hiểu đối với câu hỏi của anh ấy. :)

Vì tôi đã gặp phải vấn đề này nhiều lần trong nhiều năm và lần này tôi cần một phiên bản di động bash thuần túy mà tôi có thể sử dụng trên OSX và linux, tôi đã tiếp tục và viết một bản:

Phiên bản sống ở đây:

https://github.com/keen99/shell-functions/tree/master/resolve_path

nhưng vì lợi ích của VẬY, đây là phiên bản hiện tại (tôi cảm thấy nó đã được thử nghiệm tốt..nhưng tôi sẵn sàng nhận phản hồi!)

Có thể không khó để làm cho nó hoạt động với vỏ bourne đơn giản (sh), nhưng tôi đã không thử ... Tôi thích $ FUNCNAME quá. :)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

đây là một ví dụ cổ điển, nhờ bia:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

sử dụng hàm này và nó sẽ trả về đường dẫn -real-:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn

0

Nếu hệ điều hành của bạn hỗ trợ nó, hãy sử dụng:

realpath "./some/dir"

Và sử dụng nó trong một biến:

some_path="$(realpath "./some/dir")"

Điều đó sẽ mở rộng con đường của bạn. Đã thử nghiệm trên Ubuntu và CentOS, có thể không có sẵn trên của bạn. Một số đề xuất liên kết đọc, nhưng tài liệu cho liên kết đọc cho biết:

Lưu ý realpath (1) là lệnh được ưu tiên sử dụng cho chức năng chuẩn hóa.

Trong trường hợp mọi người thắc mắc tại sao tôi trích dẫn các biến của mình, thì đó là để bảo toàn khoảng trắng trong đường dẫn. Giống như làm realpath some pathsẽ cho bạn hai kết quả đường dẫn khác nhau. Nhưng realpath "some path"sẽ trả lại một. Thông số trích dẫn ftw :)


-1

Bạn có phải sử dụng riêng bash không? Tôi cần phải làm điều này và phát chán với sự khác biệt giữa Linux và OS X. Vì vậy, tôi đã sử dụng PHP để có một giải pháp nhanh chóng và hiệu quả.

#!/usr/bin/php <-- or wherever
<?php
{
   if($argc!=2)
      exit();
   $fname=$argv[1];
   if(!file_exists($fname))
      exit();
   echo realpath($fname)."\n";
}
?>

Tôi biết nó không phải là một giải pháp rất thanh lịch nhưng nó có hiệu quả.


Điều này không có gì nhưng trả lại đường dẫn đến tập tin nếu bạn đã biết đường dẫn đến tập tin hoặc các tập tin là trong thư mục hiện hành ... không tìm thấy các tập tin trong các thiết lập PATH
G-Man
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.