207

给定相对路径,是否有检索绝对路径的命令?

例如,我希望 $line 包含 dir 中每个文件的绝对路径./etc/

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

24 回答 24

232

试试realpath

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

为避免扩展符号链接,请使用realpath -s.

答案来自“ bash/fish 命令打印文件的绝对路径”。

于 2013-02-15T10:15:59.447 回答
131

如果您安装了 coreutils 包,您通常可以使用readlink -f relative_file_name它来检索绝对包(解决所有符号链接)

于 2010-11-14T00:46:29.820 回答
97
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPD一些解释

  1. 此脚本获取相对路径作为参数"$1"
  2. 然后我们得到该路径的dirname部分(您可以将 dir 或 file 传递给此脚本):dirname "$1"
  3. 然后我们进入这个相对目录并通过运行shell 命令cd "$(dirname "$1")获取它的绝对路径pwd
  4. 之后,我们将basename附加到绝对路径:$(basename "$1")
  5. 作为最后一步,echo我们
于 2015-07-24T08:27:54.793 回答
79

利用:

find "$(pwd)"/ -type f

获取所有文件或

echo "$(pwd)/$line"

显示完整路径(如果相对路径很重要)

于 2010-11-13T23:34:39.050 回答
36

对于它的价值,我投票支持选择的答案,但想分享一个解决方案。缺点是,它仅适用于 Linux - 在堆栈溢出之前,我花了大约 5 分钟试图找到 OSX 等价物。我确定它在那里。

在 Linux 上,您可以readlink -edirname.

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

产量

/etc/

然后你使用dirname'sisterbasename来获取文件名

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

产量

passwd

把它们放在一起。。

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

产量

/etc/passwd

如果您以目录为目标,那么您是安全的,basename不会返回任何内容,并且您最终会在最终输出中出现双斜杠。

于 2012-11-28T07:31:35.470 回答
28

我认为这是最便携的:

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

如果路径不存在,它将失败。

于 2013-12-10T16:46:01.130 回答
17

realpath可能是最好的

但 ...

最初的问题一开始就非常混乱,举一个与所述问题关系不大的例子。

所选答案实际上回答了给出的示例,而不是标题中的问题。第一个命令就是那个答案(真的吗?我怀疑),没有'/'也可以。而且我看不到第二个命令在做什么。

几个问题混合在一起:

  • 将相对路径名更改为绝对路径名,无论它表示什么,都可能没有。(通常,如果您发出诸如 之类的命令touch foo/bar,则路径名foo/bar必须为您存在,并且可能在实际创建文件之前用于计算。

  • 可能有几个绝对路径名表示同一个文件(或潜在文件),特别是因为路径上的符号链接(符号链接),但可能出于其他原因(设备可能以只读方式安装两次)。一个人可能想也可能不想明确地解析这样的符号链接。

  • 到达指向非符号链接文件或名称的符号链接链的末尾。这可能会或可能不会产生绝对路径名,具体取决于它是如何完成的。并且可能希望或可能不希望将其解析为绝对路径名。

没有选项的命令readlink foo仅在其参数是符号链接时才给出答案foo,并且该答案是该符号链接的值。没有其他链接。答案可能是相对路径:符号链接参数的值是什么。

但是,readlink具有适用于所有文件的选项(-f -e 或 -m),并为参数实际表示的文件提供一个绝对路径名(没有符号链接的路径名)。

这适用于任何不是符号链接的东西,尽管人们可能希望使用绝对路径名而不解析路径上的中间符号链接。这是由命令完成的realpath -s foo

在符号链接参数的情况下,readlink其选项将再次解析参数绝对路径上的所有符号链接,但这也将包括跟随参数值可能遇到的所有符号链接。如果您想要参数符号链接本身的绝对路径,而不是它可能链接到的任何内容,您可能不希望这样做。同样,如果foo是一个符号链接,realpath -s foo将获得一个绝对路径而不解析符号链接,包括作为参数给出的那个。

如果没有这个-s选项,除了简单地读取链接的值以及其他几件事之外,realpath它的作用与 几乎相同 。readlink我只是不清楚为什么readlink有它的选择,显然造成了不受欢迎的冗余 realpath

探索网络并没有多说什么,只是跨系统可能会有一些变化。

结论:realpath是最好的命令,具有最大的灵活性,至少对于此处要求的使用而言。

于 2013-05-17T17:24:25.810 回答
14

我最喜欢的解决方案是@EugenKonkov 的解决方案,因为它并不意味着存在其他实用程序(coreutils 包)。

但是相对路径“。”失败了。和“..”,所以这里有一个稍微改进的版本来处理这些特殊情况。

但是,如果用户没有cd进入相对路径的父目录的权限,它仍然会失败。

#! /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
}
于 2018-07-10T11:38:10.693 回答
9

尤金的回答对我来说不太管用,但确实如此:

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

旁注,您当前的工作目录不受影响。

于 2017-02-13T20:45:43.017 回答
4

恕我直言,最好的解决方案是此处发布的解决方案:https ://stackoverflow.com/a/3373298/9724628 。

它确实需要 python 才能工作,但它似乎涵盖了所有或大部分边缘情况,并且是非常便携的解决方案。

  1. 通过解析符号链接:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
  1. 或没有它:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
于 2019-02-17T23:21:18.073 回答
4

@ernest-a 相当不错的版本的改进:

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

这正确处理了路径的最后一个元素是 的情况..,在这种情况下"$(pwd)/$(basename "$1")",@ernest-a 的答案将作为accurate_sub_path/spurious_subdirectory/...

于 2019-12-27T01:04:00.573 回答
3

如果你在 Mac OS X 上使用 bash,它既不存在realpath也不它的readlink可以打印绝对路径,你可以选择编写你自己的版本来打印它。这是我的实现:

(纯重击)

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[*]}"
)
}

(使用 gawk)

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)

#!/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)
}

例子:

$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
于 2014-02-22T07:28:09.290 回答
3

在 的情况下find,最简单的方法可能是只给出搜索的绝对路径,例如:

find /etc
find `pwd`/subdir_of_current_dir/ -type f
于 2010-11-13T23:33:04.950 回答
2

类似于@ernest-a 的答案,但不影响$OLDPWD或定义新功能,您可以触发子外壳(cd <path>; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
于 2015-05-13T09:26:41.437 回答
2

我发现Eugen Konkov 的答案是最好的,因为它不需要安装任何程序。但是,对于不存在的目录,它将失败。

我编写了一个适用于不存在的目录的函数:

function getRealPath()
{
    local -i traversals=0
    currentDir="$1"
    basename=''
    while :; do
        [[ "$currentDir" == '.' ]] && { echo "$1"; return 1; }
        [[ $traversals -eq 0 ]] && pwd=$(cd "$currentDir" 2>&1 && pwd) && { echo "$pwd/$basename"; return 0; }
        currentBasename="$(basename "$currentDir")"
        currentDir="$(dirname "$currentDir")"
        [[ "$currentBasename" == '..' ]] && (( ++traversals )) || { [[ traversals -gt 0 ]] && (( traversals-- )) || basename="$currentBasename/$basename"; }
    done
}

dirname它通过向上遍历直到cd成功,然后返回当前目录以及被删除的所有内容来解决不存在目录的问题dirname

于 2021-03-20T13:17:59.143 回答
1

他们所说的,除了find $PWD或(在 bash 中)find ~+更方便一些。

于 2010-11-13T23:44:12.763 回答
1

我找不到可以在 Mac OS Catalina、Ubuntu 16 和 Centos 7 之间完美移植的解决方案,所以我决定使用 python inline 来完成它,它适用于我的 bash 脚本。

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

to_abs_path "/some_path/../secrets"
于 2020-07-04T10:47:18.527 回答
1

您可以对任何相对路径 $line 使用 bash 字符串替换:

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

基本的字符串前面替换遵循公式
${string/#substring/replacement}
在这里很好地讨论:https ://www.tldp.org/LDP/abs/html/string-manipulation.html

当我们希望它成为我们查找/替换的字符串的一部分时,该\字符否定了。/

于 2020-04-30T04:46:02.400 回答
1
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
于 2016-03-14T20:32:28.053 回答
1

如果相对路径是目录路径,那么试试我的,应该是最好的:

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

echo $absPath
于 2015-10-13T09:37:55.770 回答
1

这是一个相当短的函数,可用于仅使用 POSIX shell 和readlink语义来完全绝对化和规范化任何给定的输入路径:

canonicalize_path() {
    (
        FILEPATH="$1"
        for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
        do
            cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
            if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
            then
                break
            fi
        done
        cd -P "." || return $?
        echo "$(pwd)/${FILEPATH}"
    )
}

如果引用的文件不存在,则只会解析通向最终文件名的目录路径。如果指向文件路径的任何目录不存在,cd将返回错误。这恰好是readlink -f它试图模仿的 GNU/Linux 命令的确切语义。

在 bash/zsh 中,您还可以将数字列表压缩为 just{1..8}或类似。选择 8 的数量是因为这是多年来 Linux 中的最大限制,然后在 4.2 版中将其更改为整个路径的 40 分辨率的总限制。如果达到分辨率限制,代码将不会失败,而是返回最后考虑的路径——[ -L "${FILEPATH}" ]然而,可以添加显式检查来检测这种情况。

通过简单地删除函数和子shell包装器,此代码也可以轻松地重用,以确保当前工作目录与文件系统中执行脚本的位置匹配(shell脚本的常见要求):

FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
do
    cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
    if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
    then
        break
    fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"
于 2020-10-21T15:46:21.717 回答
0

如果要将包含相对路径的变量转换为绝对路径,则可以:

   dir=`cd "$dir"`

"cd" 在不改变工作目录的情况下回显,因为在子 shell 中执行。

于 2016-08-25T05:21:42.133 回答
0

这是所有其他解决方案的链式解决方案,例如,当realpath由于未安装或由于错误代码退出而失败时,则尝试下一个解决方案,直到找到正确的路径。

#!/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")" "${?}"
于 2019-07-05T04:23:47.340 回答
0

基于@EugenKonkov的这个回答@HashChange的这个回答,我的回答结合了前者的简洁性和后者的处理。我相信下面的所有选项都依赖于基本的Shell 命令语言 POSIX 标准...

使用dirnameand basename,一个选项是:

absPathDirname()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
    echo "`cd "${1}"; pwd`${2}";
}

不使用dirnameor basename,另一个简短的选项是:

absPathMinusD()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
    echo "`cd "${1:-.}"; pwd`${2}";
}

我会推荐上面两个选项之一,其余的只是为了好玩......

grep 版本:

absPathGrep()
{
    echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}

作为“使用 shell 的有限 RegEx 可以做什么”的一个有趣示例:

absPathShellReplace()
{
    E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
    DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
    echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}
于 2021-05-03T13:22:42.057 回答