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

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个回答

275

尝试使用realpath函数。

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

为了避免扩展符号链接,请使用 realpath -s
答案来自于"bash/fish command to print absolute path to a file"。

17
在 Mac(OS X 10.11“El Capitan”)上似乎没有realpath命令可用。 :-( - András Aszódi
在CentOS 6上似乎也没有realpath命令。 - user5359531
10
在 macOS 上,运行 brew install coreutils 命令会安装 realpath - Kevin Chen
1
在我的Ubuntu 18.04上,realpath已经存在了。我不需要单独安装它。 - Asclepius
1
令人惊讶的是,realpath 在 Git for Windows 上是可用的(至少对我来说是这样)。 - Andrew Keeton
显示剩余5条评论

147
如果您安装了coreutils软件包,通常可以使用readlink -f relative_file_name来检索绝对路径(解析所有符号链接)。

4
这种行为与用户的要求有些不同,它还会遵循并解析路径中任何位置的递归符号链接。在某些情况下,您可能不希望出现这种情况。 - ffledgling
2
@BradPeabody 如果你在Mac上安装了homebrew的coreutils brew install coreutils,这个命令是可以工作的。然而,可执行文件前面会加上一个g:greadlink -f relative_file_name - Miguel Isla
3
请注意,readlink(1)的手册页面在其描述的第一句话中指出:“请注意 realpath(1) 是用于规范化功能的首选命令。” - josch
1
您可以使用“-e”而不是“-f”来检查文件/目录是否存在。 - Iceman
1
仅使用"."这样基本的东西就完全失败了。 - Szczepan Hołyszewski
最好的解决方案是在shell内部完成,而不需要外部软件。 - Bill Gale

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

更新:一些说明

  1. 这个脚本接受相对路径作为参数 "$1"
  2. 然后我们获取该路径的dirname部分(你可以将目录或文件传递给这个脚本):dirname "$1"
  3. 接下来,我们cd "$(dirname "$1")进入这个相对目录并通过运行pwd shell命令获取其绝对路径
  4. 之后我们将basename附加到绝对路径上:$(basename "$1")
  5. 最后一步,我们echo输出它

7
"readlink" 是 Linux 上的简单解决方案,但这个解决方案同样适用于 OSX,所以加1。 - thetoolman
6
@josch:这个问题不是关于符号链接的解析。但如果你想做到这一点,你可以在pwd命令中提供-P选项:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"。请注意,这段话已经被翻译成中文。 - Eugen Konkov
9
我喜欢这个答案,但仅当用户被允许进入该目录时才有效。这可能并非总是可行。 - Matthias B
2
我的个人最爱 :) 不幸的是,当我将“.”和“..”作为相对路径输入时,它失败了。稍微改进的版本:https://dev59.com/tm855IYBdhLWcg3wuG-W#51264222 - hashchange
1
无法正确处理已经是绝对路径的路径。 - Szczepan Hołyszewski
显示剩余5条评论

83

使用:

find "$(pwd)"/ -type f

获取所有文件或

echo "$(pwd)/$line"

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


38

就我所知,我投了被选中的答案,但我想分享一种解决方案。缺点是它只适用于Linux——在来到Stack Overflow之前,我花了大约5分钟尝试寻找OSX的等效方法。不过我相信它一定存在。

在Linux中,你可以使用readlink -edirname结合使用。

$(dirname $(readlink -e ../../../../etc/passwd))
产生。
/etc/

然后你使用dirname的姐妹函数basename, 只获取文件名

$(basename ../../../../../passwd)
产出。
passwd

把所有东西放在一起...

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

如果你的目标是一个目录,basename 函数将返回空值,并且你最终的输出结果只会有双斜杠。


使用dirnamereadlinkbasename的出色入口。这帮助我获取符号链接的绝对路径,而不是其目标。 - kevinarpe
当你想要返回符号链接的路径时,它无法工作(而我恰好需要这样做...)。 - Tomáš Zato
你怎么可能找到一个不存在的路径的绝对路径呢? - synthesizerpatel
@synthesizerpatel 我觉得这很容易;如果我在/home/GKFX目录下,然后输入touch newfile,在我按回车键之前,你就可以推断出我的意思是“创建/home/GKFX/newfile”,这是一个绝对路径,指向一个尚不存在的文件。 - GKFX

30

我认为这是最便携的:

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

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


4
无需再次返回CD。请参见https://dev59.com/gm865IYBdhLWcg3wOcHy#21188136。在我看来,你的回答是这一页中最好的。对于那些感兴趣的人,该链接提供了为什么此解决方案有效的解释。 - peterh
这不是很便携,dirname 是 GNU 核心实用程序,我认为并不常见于所有的 Unix 系统。 - einpoklum
@einpoklum dirname是POSIX标准实用程序,请参见此处:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html - Ernest A
天啊,谢谢。我已经试了一整天来修复使用${1##*/}的版本,现在我用basename "$1"替换了那个垃圾,看起来终于可以正确处理以/结尾的路径了。 - l3l_aze
显示剩余2条评论

19

realpath可能是最佳选择。

但是……

最初的问题非常混乱,示例与所述问题关系不大。

所选答案实际上回答了给出的示例,而不是标题中的问题。第一个命令就是答案(确实吗?我怀疑),并且可以不带“/'”使用。我看不出第二个命令在做什么。

有几个问题被混合:

  • 将相对路径名更改为绝对路径名,无论它表示什么,可能是空的。 (通常,如果您发出类似于 touch foo/bar 的命令,则必须存在路径名 foo/bar,并且可能在计算中使用,然后才会创建文件。

  • 可能有几个绝对路径名表示相同的文件(或潜在文件),特别是由于路径上的符号链接(symlinks),但可能是出于其他原因 (设备可能被挂载两次作为只读)。人们可能想要显式解决此类符号链接。

  • 到达一系列符号链接的末端,指向非符号链接文件或名称。这可能会产生绝对路径名,也可能不会,具体取决于如何完成。人们可以,或者不想将其解析为绝对路径名。

命令readlink foo没有选项只有当它的参数foo是一个符号链接时才给出答案,该答案是该符号链接的值。不遵循其他链接。答案可能是相对路径:符号链接参数的值是什么就是什么。

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

这对于任何不是符号链接的东西都可以正常工作,尽管人们可能希望使用绝对路径名而不解析中间的链接,因此realpath可能是最佳选择。

在路径中使用符号链接。这可以通过命令realpath -s foo来完成。

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

没有-s选项,realpathreadlink基本相同,除了简单读取链接的值以及其他几件事情。我只是不清楚为什么readlink有其选项,似乎会创建不必要的冗余与realpath

探索网络并没有说太多,除了各种系统可能存在差异。

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


readlink的man页面说:“请注意,realpath(1)是用于规范化功能的首选命令。” - jarno
@jarno 很有趣。我的手册确实写着这个,但是日期是2021年4月。你知道那行代码是什么时候加入的吗? - babou
commit是在2017年创建的。 - jarno
@jarno 谢谢。我应该学会搜索那种信息。嗯,四年后得到官方支持的答案真是太好了 :-)。可惜没有徽章可以获得。 - babou

18

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

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

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

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

+1 很好!但是在这种情况下有一个错误: to-abs-path /tmp/../tmp/../var //var相比之下,正确的预期输出应该是: realpath /tmp/../tmp/../var /var - Dmitry Shevkoplyas

9
尽管Eugen的回答对我不起作用,但是这个可以:
    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

顺便提一下,您目前的工作目录不会受到影响。


4
@ernest-a的版本已经很好了,这里提供一个改进版本:
absolute_path() {
    cd "$(dirname "$1")"
    case $(basename $1) in
        ..) echo "$(dirname $(pwd))";;
        .)  echo "$(pwd)";;
        *)  echo "$(pwd)/$(basename $1)";;
    esac
}

这样可以正确处理路径的最后一个元素为 .. 的情况,此时 @ernest-a 的答案中的 "$(pwd)/$(basename "$1")" 将被解析为 accurate_sub_path/spurious_subdirectory/..


不错的尝试,但是:(1)绝对路径 /tmp/../tmp/../var //var (2)目标必须存在。 - Dmitry Shevkoplyas

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