如何获取相对路径的绝对路径

241

有没有一条命令可以根据相对路径获取绝对路径?

例如,我想让$line包含dir目录下每个文件的绝对路径:./etc/

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

8
可能是之前讨论的问题的重复:将相对路径转换为绝对路径,或者这个 - Dennis Williamson
1
可能是重复的问题:bash/fish命令打印文件的绝对路径 - Trevor Boyd Smith
迄今为止比列出的任何解决方案都要好得多的解决方案在这里:如何在Unix中将相对路径转换为绝对路径 - Daniel Genin
你可能想看看这个链接,因为它可以帮助你在Git仓库中相对于存储库路径配置脚本的路径。 - Nae
26个回答

4

依我看,最好的解决方案是在此处发布的一个:https://dev59.com/_HVC5IYBdhLWcg3wdw65#3373298

它确实需要使用Python才能工作,但它似乎涵盖了所有或大多数边缘情况,并且是非常可移植的解决方案。

  1. 带解析符号链接:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file

2. 或者没有它:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

3

如果使用find命令,最简单的方法是直接提供要搜索的绝对路径,例如:

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

3

如果你在Mac OS X上使用bash,该系统没有realpath或其readlink无法打印绝对路径,那么你只能选择编写自己的版本来打印它。

(纯bash)

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
}

(纯BSK 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

readlink 可以处理缺失的文件/文件夹,就像这个例子:readlink -m I/am/.//..//the/./god/../of///.././war 会产生与你的输出相同的结果: /home/dmitry/I/the/war - Dmitry Shevkoplyas

2
类似于@ernest-a的回答,但不会影响$OLDPWD或定义新函数,您可以启动一个子shell (cd

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删除的所有内容。

2

我曾试图在Mac OS Catalina,Ubuntu 16和Centos 7之间找到一个方便地跨平台的解决方案,但最终我决定使用内置的Python实现,并且它对我的Bash脚本非常有效。

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

to_abs_path "/some_path/../secrets"

1
如果相对路径是目录路径,则尝试使用我的路径,应该是最佳选择:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath

此解决方案仅适用于bash,请参见https://dev59.com/RW435IYBdhLWcg3w0Trc#5193087。 - Michael

1
"BLUF"的英文意思是"Bottom Line Up Front",翻译成中文就是"底线优先"。
cd $relative_path ; pwd

这里是一个符合POSIX标准(我认为)的解释,所以它应该可以在任何平台上使用。
当然,这是可编程的,但我认为将其分解可能会使一些人更容易理解/修改为特定的用例。
您可以使用whichlocatefind、完整路径等方法。 x=your_file_name
$ x="nvi" 

“file” 很容易指示符号链接。
$ file -h `which $x`
/usr/local/bin/nvi: symbolic link to ../Cellar/nvi/1.81.6_5/bin/nvi

接下来,对输出进行一些处理,以便获得一个“完整”的相对路径。
在这个例子中,我们只需要删除中间部分即可。
注意大写字母Y和小写字母x的区别。可能有更简洁的方法来解决这个问题。
$ Y=$(file -h `which $x` | sed "s/$x: symbolic link to //")


$ echo $Y
/usr/local/bin/../Cellar/nvi/1.81.6_5/bin/nvi

使用dirname,我们只获取路径部分。切换到该路径并且文件名应该会自动清理。
$ cd `dirname $Y` ; pwd
/usr/local/Cellar/nvi/1.81.6_5/bin

这让我们想到了旧的UNIX技巧,即通过在括号/子shell中执行操作来“幽灵遍历”目录。这样做可以在完成后将我们返回到当前目录。
我们可以把实际的文件名再添加回去,以保证完整性。
此外,为了额外加分,ls会确保绝对路径是有效的。
$ ( cd `dirname ${Y}` ; ls `pwd`/${x} )
/usr/local/Cellar/nvi/1.81.6_5/bin/nvi

所以 /usr/local/bin/nvi 实际上是 /usr/local/Cellar/nvi/1.81.6_5/bin/nvi 一个简化的示例,可以快速“转换”路径:
$ (cd /usr/local/bin/../Cellar/nvi/1.81.6_5/bin ; pwd)
/usr/local/Cellar/nvi/1.81.6_5/bin

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html https://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html


1

使用 Python 3 的先前答案的更新

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

允许

to_abs_path "./../some_file.txt"

1

除了 find $PWD 或者(在 bash 中)find ~+ 这两个命令稍微方便一些,其他都是他们所说的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接