Cách lấy đường dẫn tuyệt đối đã cho


159

Có một lệnh để lấy đường dẫn tuyệt đối cho đường dẫn tương đối không?

Ví dụ: tôi muốn $ line chứa đường dẫn tuyệt đối của mỗi tệp trong thư mục ./etc/

find ./ -type f | while read line; do
   echo $line
done


1
không trùng lặp, nhưng tương tự
mpapis


Một giải pháp tốt hơn nhiều so với bất kỳ giải pháp nào được liệt kê cho đến nay là ở đây cách chuyển đổi tương đối-đường dẫn đến đường dẫn tuyệt đối trong đường dẫn unix
Daniel Genin

Câu trả lời:


66

sử dụng:

find "$(pwd)"/ -type f

để có được tất cả các tập tin hoặc

echo "$(pwd)/$line"

để hiển thị đường dẫn đầy đủ (nếu đường dẫn tương đối quan trọng)


2
Điều gì xảy ra nếu tôi chỉ định một đường dẫn tương đối trong tìm kiếm ??
nubme

17
sau đó xem các liên kết trong nhận xét cho câu hỏi của bạn, cách tốt nhất có lẽ là 'readlink -f $ line'
mpapis

3
Tại sao sử dụng $(pwd)thay $PWDthế? (vâng, tôi biết pwdlà một nội dung)
Adam Katz

178

Hãy thử realpath.

~ $ sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

Để tránh mở rộng liên kết tượng trưng, ​​sử dụng realpath -s.

Câu trả lời đến từ " lệnh bash / fish để in đường dẫn tuyệt đối đến một tệp ".


11
realpathdường như không khả dụng trên Mac (OS X 10.11 "El Capitan"). :-(
Laryx Decidua

realpathdường như cũng không khả dụng trên CentOS 6
user5359531

6
trên osx, brew install coreutilssẽ mang vàorealpath
Kevin Chen

Trên Ubuntu 18.04 của tôi, realpathđã có mặt. Tôi đã không phải cài đặt nó một cách riêng biệt.
Acumenus

Đáng ngạc nhiên, realpathcó sẵn trên Git cho Windows (ít nhất là đối với tôi).
Andrew Keeton

104

Nếu bạn đã cài đặt gói coreutils, bạn thường có thể sử dụng readlink -f relative_file_nameđể truy xuất gói tuyệt đối (với tất cả các liên kết tượng trưng được giải quyết)


3
Hành vi cho điều này hơi khác so với những gì người dùng yêu cầu, nó cũng sẽ tuân theo và giải quyết các liên kết đệ quy ở bất cứ đâu trong đường dẫn. Bạn có thể không muốn điều đó trong một số trường hợp.
đánh bại

1
@BradPeabody Điều này không hoạt động trong máy Mac nếu bạn cài đặt coreutils từ homebrew brew install coreutils. Tuy nhiên, việc thực thi được chuẩn bị bởi ag:greadlink -f relative_file_name
Miguel Isla

2
Lưu ý rằng trang thủ công của readlink (1) có câu đầu tiên trong phần mô tả của nó: "Lưu ý realpath (1) là lệnh ưa thích để sử dụng cho chức năng chuẩn hóa."
josch

1
bạn có thể sử dụng -e thay vì -f để kiểm tra xem tệp / thư mục có tồn tại hay không
Iceman

69
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

CẬP NHẬT Một số giải thích

  1. Kịch bản này lấy đường dẫn tương đối làm đối số "$1"
  2. Sau đó, chúng tôi nhận được phầ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 thư mục tương đối này và nhận đường dẫn tuyệt đối cho nó bằng cách chạy pwdlệnh shell
  4. Sau đó, chúng tôi nối tên cơ sở vào đường dẫn tuyệt đối:$(basename "$1")
  5. Như bước cuối cùng chúng ta echo

8
Câu trả lời này sử dụng thành ngữ Bash tốt nhất
Rondo

2
readlink là giải pháp đơn giản cho linux, nhưng giải pháp này cũng hoạt động trên OSX, vì vậy +1
thetoolman

1
Kịch bản này không tương đương với những gì realpathhoặc readlink -flàm. Chẳng hạn, nó không hoạt động trên các đường dẫn trong đó thành phần cuối cùng là một liên kết tượng trưng.
josch

2
@josch: Câu hỏi không phải là về việc giải quyết symlink. Nhưng nếu bạn muốn làm điều đó, bạn có thể cung cấp -Ptùy chọn để ra pwdlệnh:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Eugen Konkov

4
Tôi thích câu trả lời, nhưng nó chỉ hoạt động, nếu người dùng được phép cd vào thư mục. Điều này có thể không phải lúc nào cũng có thể.
Matthias B

34

Đối với những gì nó có giá trị, tôi đã bỏ phiếu cho câu trả lời đã được chọn, nhưng muốn chia sẻ một giải pháp. Nhược điểm là, chỉ có Linux - Tôi đã dành khoảng 5 phút để cố gắng tìm OSX tương đương trước khi đến với Stack tràn. Tôi chắc chắn rằng nó ở ngoài đó mặc dù.

Trên Linux, bạn có thể sử dụng readlink -esong song với dirname.

$(dirname $(readlink -e ../../../../etc/passwd))

sản lượng

/etc/

Và sau đó bạn sử dụng dirnamechị gái, basenameđể chỉ lấy tên tệp

$(basename ../../../../../passwd)

sản lượng

passwd

Đặt nó tất cả cùng nhau..

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

sản lượng

/etc/passwd

Bạn an toàn nếu bạn đang nhắm mục tiêu một thư mục, basenamesẽ không trả lại bất cứ thứ gì và bạn sẽ kết thúc bằng dấu gạch chéo kép ở đầu ra cuối cùng.


Entry tuyệt vời với dirname, readlink, và basename. Điều đó giúp tôi có được đường dẫn tuyệt đối của một liên kết tượng trưng - không phải mục tiêu của nó.
kevinarpe

Không hoạt động khi bạn muốn trả lại đường dẫn đến các liên kết tượng trưng (điều mà tôi tình cờ cần phải làm ...).
Tomáš Zato - Phục hồi Monica

Làm thế nào bạn có thể tìm thấy con đường tuyệt đối đến một con đường không tồn tại?
tổng

@synthesizerpatel Khá dễ dàng, tôi đã có thể nghĩ rằng; nếu tôi vào /home/GKFXvà tôi gõ touch newfile, thì trước khi tôi nhấn, bạn có thể hiểu rằng tôi có nghĩa là "tạo / nhà / GKFX / newfile", đây là một đường dẫn tuyệt đối đến một tệp chưa tồn tại.
GKFX

25

Tôi nghĩ rằng đây là di động nhất:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

Nó sẽ thất bại nếu đường dẫn không tồn tại mặc dù.


3
Không cần phải quay lại cd. Xem stackoverflow.com/a/21188136/1504556 . Bạn là câu trả lời tốt nhất trên trang này, IMHO. Đối với những người quan tâm, liên kết đưa ra một lời giải thích về lý do tại sao giải pháp này hoạt động.
peterh

Đây không phải là rất dễ mang theo, dirnamelà một tiện ích lõi GNU, không phổ biến đối với tất cả các unixen tôi tin.
einpoklum

@einpoklum dirnamelà một tiện ích tiêu chuẩn POSIX, xem tại đây: pubs.opengroup.org/onlinepub/9699919799/utilities/dirname.html
Ernest A

Ôi Chúa ơi, cảm ơn bạn. Tôi đã cố gắng sửa phiên bản sử dụng ${1##*/}trong một ngày và bây giờ tôi đã thay thế thùng rác basename "$1"đó bằng cách cuối cùng có thể xử lý đúng các đường dẫn kết thúc bằng /.
l3l_aze

NB, điều này không làm đúng với một đường dẫn kết thúc vào../..
Alex Coventry

16

realpath có lẽ là tốt nhất

Nhưng ...

Câu hỏi ban đầu rất bối rối khi bắt đầu, với một ví dụ liên quan đến câu hỏi như đã nêu.

Câu trả lời được chọn thực sự trả lời ví dụ đã cho, và hoàn toàn không phải câu hỏi trong tiêu đề. Lệnh đầu tiên là câu trả lời đó (có thực sự không? Tôi nghi ngờ), và có thể làm tốt mà không cần '/'. Và tôi không thấy lệnh thứ hai đang làm gì.

Một số vấn đề được trộn lẫn:

  • thay đổi một tên đường dẫn tương đối thành một tên tuyệt đối, bất kể nó biểu thị điều gì, có thể không có gì. ( Thông thường, nếu bạn ban hành một lệnh như touch foo/bar, tên đường dẫn foo/barphải tồn tại cho bạn và có thể được sử dụng trong tính toán, trước khi tệp thực sự được tạo. )

  • có thể có một số tên đường dẫn tuyệt đối biểu thị cùng một tệp (hoặc tệp tiềm năng), đáng chú ý là do các liên kết tượng trưng (symlink) trên đường dẫn, nhưng có thể vì các lý do khác (một thiết bị có thể được gắn hai lần chỉ đọc). Người ta có thể hoặc không muốn giải quyết các liên kết như vậy.

  • đi đến cuối chuỗi các liên kết tượng trưng đến một tệp hoặc tên không liên kết. Điều này có thể hoặc không thể mang lại một tên đường dẫn tuyệt đối, tùy thuộc vào cách nó được thực hiện. Và người ta có thể, hoặc có thể không muốn giải quyết nó thành một tên đường dẫn tuyệt đối.

Lệnh readlink fookhông có tùy chọn chỉ đưa ra câu trả lời nếu đối số của nó foolà liên kết tượng trưng và câu trả lời đó là giá trị của liên kết tượng trưng đó. Không có liên kết khác được theo sau. Câu trả lời có thể là một đường dẫn tương đối: bất cứ giá trị nào của đối số symlink.

Tuy nhiên, readlinkcó các tùy chọn (-f -e hoặc -m) sẽ hoạt động cho tất cả các tệp và đặt một tên đường dẫn tuyệt đối (tên không có liên kết tượng trưng) cho tệp thực sự được biểu thị bằng đối số.

Điều này hoạt động tốt cho bất cứ điều gì không phải là một liên kết tượng trưng, ​​mặc dù người ta có thể muốn sử dụng một tên đường dẫn tuyệt đối mà không giải quyết các liên kết trung gian trên đường dẫn. Điều này được thực hiện bởi lệnhrealpath -s foo

Trong trường hợp đối số symlink, readlinkvới các tùy chọn của nó sẽ lại giải quyết tất cả các liên kết tượng trưng trên đường dẫn tuyệt đối đến đối số, nhưng điều đó cũng sẽ bao gồm tất cả các liên kết tượng trưng có thể gặp phải bằng cách tuân theo giá trị đối số. Bạn có thể không muốn rằng nếu bạn muốn một đường dẫn tuyệt đối đến chính liên kết đối số, hơn là bất cứ điều gì nó có thể liên kết đến. Một lần nữa, nếu foolà một liên kết tượng trưng, realpath -s foosẽ có được một đường dẫn tuyệt đối mà không giải quyết các liên kết tượng trưng, ​​bao gồm cả một liên kết được đưa ra làm đối số.

Không có -stùy chọn, realpathsẽ thực hiện khá nhiều readlink, ngoại trừ việc chỉ đọc giá trị của một liên kết, cũng như một số thứ khác. Nó chỉ không rõ ràng cho tôi tại sao readlinkcó các tùy chọn của nó, tạo ra sự dư thừa không mong muốn với realpath .

Khám phá web không nói gì nhiều, ngoại trừ việc có thể có một số biến thể trên các hệ thống.

Kết luận: realpathlà lệnh tốt nhất để sử dụng, với tính linh hoạt nhất, ít nhất là cho việc sử dụng được yêu cầu ở đây.


10

Giải pháp yêu thích của tôi là một bằng @EugenKonkov bởi vì nó không bao hàm sự hiện diện của các tiện ích khác (gói coreutils).

Nhưng nó đã thất bại cho các đường dẫn tương đối "." và "..", vì vậy đây là phiên bản cải tiến nhẹ xử lý các trường hợp đặc biệt này.

Nó vẫn không thành công nếu người dùng không có quyền cdvào thư mục mẹ của đường dẫn tương đối.

#! /bin/sh

# Takes a path argument and returns it as an absolute path. 
# No-op if the path is already absolute.
function to-abs-path {
    local target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

7

Câu trả lời của Eugen không hoàn toàn phù hợp với tôi nhưng điều này đã làm:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

Lưu ý bên, thư mục làm việc hiện tại của bạn không bị ảnh hưởng.


Lưu ý: đó là kịch bản. trong đó $ 1 là đối số đầu tiên cho nó
Eugen Konkov

5

Giải pháp tốt nhất, imho, là giải pháp được đăng ở đây: https://stackoverflow.com/a/3373298/9724628 .

Nó đòi hỏi python để làm việc, nhưng nó dường như bao gồm tất cả hoặc hầu hết các trường hợp cạnh và là giải pháp rất di động.

  1. Với việc giải quyết các liên kết tượng trưng:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
  1. hoặc không có nó:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

3

Trong trường hợp find, có lẽ dễ nhất là chỉ đưa ra đường dẫn tuyệt đối để nó tìm kiếm, ví dụ:

find /etc
find `pwd`/subdir_of_current_dir/ -type f

3

Nếu bạn đang sử dụng bash trên Mac OS X mà không phải đã realpath tồn tại và cũng không nó readlink có thể in các đường dẫn tuyệt đối, bạn có thể có lựa chọn nào khác để mã phiên bản của riêng bạn để in nó. Đây là cách thực hiện của tôi:

(bash tinh khiết)

abspath(){
  local thePath
  if [[ ! "$1" =~ ^/ ]];then
    thePath="$PWD/$1"
  else
    thePath="$1"
  fi
  echo "$thePath"|(
  IFS=/
  read -a parr
  declare -a outp
  for i in "${parr[@]}";do
    case "$i" in
    ''|.) continue ;;
    ..)
      len=${#outp[@]}
      if ((len==0));then
        continue
      else
        unset outp[$((len-1))] 
      fi
      ;;
    *)
      len=${#outp[@]}
      outp[$len]="$i"
      ;;
    esac
  done
  echo /"${outp[*]}"
)
}

(sử dụng chim ưng)

abspath_gawk() {
    if [[ -n "$1" ]];then
        echo $1|gawk '{
            if(substr($0,1,1) != "/"){
                path = ENVIRON["PWD"]"/"$0
            } else path = $0
            split(path, a, "/")
            n = asorti(a, b,"@ind_num_asc")
            for(i in a){
                if(a[i]=="" || a[i]=="."){
                    delete a[i]
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            m = 0
            while(m!=n){
                m = n
                for(i=1;i<=n;i++){
                    if(a[b[i]]==".."){
                        if(b[i-1] in a){
                            delete a[b[i-1]]
                            delete a[b[i]]
                            n = asorti(a, b, "@ind_num_asc")
                            break
                        } else exit 1
                    }
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            if(n==0){
                printf "/"
            } else {
                for(i=1;i<=n;i++){
                    printf "/"a[b[i]]
                }
            }
        }'
    fi
}

(bsd awk thuần túy)

#!/usr/bin/env awk -f
function abspath(path,    i,j,n,a,b,back,out){
  if(substr(path,1,1) != "/"){
    path = ENVIRON["PWD"]"/"path
  }
  split(path, a, "/")
  n = length(a)
  for(i=1;i<=n;i++){
    if(a[i]==""||a[i]=="."){
      continue
    }
    a[++j]=a[i]
  }
  for(i=j+1;i<=n;i++){
    delete a[i]
  }
  j=0
  for(i=length(a);i>=1;i--){
    if(back==0){
      if(a[i]==".."){
        back++
        continue
      } else {
        b[++j]=a[i]
      }
    } else {
      if(a[i]==".."){
        back++
        continue
      } else {
        back--
        continue
      }
    }
  }
  if(length(b)==0){
    return "/"
  } else {
    for(i=length(b);i>=1;i--){
      out=out"/"b[i]
    }
    return out
  }
}

BEGIN{
  if(ARGC>1){
    for(k=1;k<ARGC;k++){
      print abspath(ARGV[k])
    }
    exit
  }
}
{
  print abspath($0)
}

thí dụ:

$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war

1

Những gì họ nói, ngoại trừ find $PWDhoặc (trong bash) find ~+sẽ thuận tiện hơn một chút.


1

Tương tự như câu trả lời của @ ernest-a nhưng không ảnh hưởng $OLDPWDhoặc xác định chức năng mới, bạn có thể kích hoạt một mạng con(cd <path>; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups

1

Nếu đường dẫn tương đối là đường dẫn thư mục, thì hãy thử của tôi, nên là tốt nhất:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath

Giải pháp này chỉ hoạt động cho bash, xem thêm stackoverflow.com/a/5193087/712014
Michael

1
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

3
Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp ngắn hạn có giới hạn. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
Toby Speight

1

Một cải tiến cho phiên bản khá hay của @ ernest-a:

absolute_path() {
    cd "$(dirname "$1")"
    case $(basename $1) in
        ..) echo "$(dirname $(pwd))";;
        .)  echo "$(pwd)";;
        *)  echo "$(pwd)/$(basename $1)";;
    esac
}

Điều này xử lý chính xác với trường hợp phần tử cuối cùng của đường dẫn .., trong trường hợp đó "$(pwd)/$(basename "$1")", câu trả lời trong @ ernest-a sẽ xuất hiện dưới dạng accurate_sub_path/spurious_subdirectory/...


0

Nếu bạn muốn chuyển đổi một biến chứa đường dẫn tương đối thành một đường tuyệt đối, thì điều này hoạt động:

   dir=`cd "$dir"`

"cd" lặp lại mà không thay đổi thư mục làm việc, bởi vì được thực hiện ở đây trong một vỏ con.


2
Trên bash-4.3-p46, điều này không hoạt động: vỏ in một dòng trống khi tôi chạydir=`cd ".."` && echo $dir
Michael

0

Đây là một giải pháp được xâu chuỗi từ tất cả những người khác, ví dụ, khi realpaththất bại, vì nó không được cài đặt hoặc vì nó thoát với mã lỗi, sau đó, giải pháp tiếp theo được thử cho đến khi nó đi đúng đường.

#!/bin/bash

function getabsolutepath() {
    local target;
    local changedir;
    local basedir;
    local firstattempt;

    target="${1}";
    if [ "$target" == "." ];
    then
        printf "%s" "$(pwd)";

    elif [ "$target" == ".." ];
    then
        printf "%s" "$(dirname "$(pwd)")";

    else
        changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
        firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
        firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;

        # If everything fails... TRHOW PYTHON ON IT!!!
        local fullpath;
        local pythoninterpreter;
        local pythonexecutables;
        local pythonlocations;

        pythoninterpreter="python";
        declare -a pythonlocations=("/usr/bin" "/bin");
        declare -a pythonexecutables=("python" "python2" "python3");

        for path in "${pythonlocations[@]}";
        do
            for executable in "${pythonexecutables[@]}";
            do
                fullpath="${path}/${executable}";

                if [[ -f "${fullpath}" ]];
                then
                    # printf "Found ${fullpath}\\n";
                    pythoninterpreter="${fullpath}";
                    break;
                fi;
            done;

            if [[ "${pythoninterpreter}" != "python" ]];
            then
                # printf "Breaking... ${pythoninterpreter}\\n"
                break;
            fi;
        done;

        firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
        # printf "Error: Could not determine the absolute path!\\n";
        return 1;
    fi
}

printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"

0

Bạn có thể sử dụng thay thế chuỗi bash cho bất kỳ đường dẫn tương đối $ line:

line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line

Sự thay thế trước chuỗi cơ bản tuân theo công thức
$ {chuỗi / # chuỗi con / thay thế}
được thảo luận tốt ở đây: https://www.tldp.org/LDP/abs/html/opes-manipulation.html

\tự phủ nhận /khi chúng ta muốn nó là một phần của chuỗi mà chúng ta tìm / thay thế.


0

Tôi không thể tìm thấy một giải pháp có thể di động gọn gàng giữa Mac OS Catalina, Ubuntu 16 và Centos 7, vì vậy tôi đã quyết định thực hiện với python inline và nó hoạt động tốt cho các tập lệnh bash của tôi.

to_abs_path() {
  python -c "import os; print os.path.abspath('$1')"
}

to_abs_path "/some_path/../secrets"
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.