如何检索给定相对路径的绝对路径
Posted
技术标签:
【中文标题】如何检索给定相对路径的绝对路径【英文标题】:How to retrieve absolute path given relative 【发布时间】:2011-05-09 16:24:14 【问题描述】:在给定相对路径的情况下,是否有检索绝对路径的命令?
例如,我希望 $line 包含 dir ./etc/
中每个文件的绝对路径
find ./ -type f | while read line; do
echo $line
done
【问题讨论】:
Converting relative path into absolute path 或 this 的可能重复项。 bash/fish command to print absolute path to a file 的可能副本 比目前列出的任何解决方案都好得多的解决方案在这里how-to-convert-relative-path-to-absolute-path-in-unix 您可能想查看this,因为它可能有助于在您的脚本中配置相对于 git 存储库中的存储库路径的路径。 【参考方案1】:试试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”。
【讨论】:
realpath
在 Mac 上似乎不可用(OS X 10.11 “El Capitan”)。 :-(
realpath
在 CentOS 6 上似乎也不可用
在 osx 上,brew install coreutils
将引入 realpath
在我的 Ubuntu 18.04 上,realpath
已经存在。我不必单独安装它。
令人惊讶的是,realpath
在 Git for Windows 上可用(至少对我而言)。【参考方案2】:
如果您安装了 coreutils 软件包,您通常可以使用readlink -f relative_file_name
来检索绝对值(解决所有符号链接)
【讨论】:
此行为与用户要求的略有不同,它还将遵循并解析路径中任何位置的递归符号链接。在某些情况下,您可能不希望这样。 @BradPeabody 如果您从 homebrewbrew install coreutils
安装 coreutils,这确实可以在 Mac 中使用。但是可执行文件前面有一个 g:greadlink -f relative_file_name
请注意,readlink(1) 的手册页描述的第一句话是:“注意 realpath(1) 是用于规范化功能的首选命令。”
你可以使用 -e 代替 -f 来检查文件/目录是否存在【参考方案3】:
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD一些解释
-
此脚本获取相对路径作为参数
"$1"
然后我们得到该路径的 dirname 部分(您可以将 dir 或文件传递给此脚本):dirname "$1"
然后我们cd "$(dirname "$1")
进入这个相对目录并通过运行pwd
shell 命令获取它的绝对路径
之后我们将 basename 附加到绝对路径:$(basename "$1")
作为最后一步,我们echo
它
【讨论】:
readlink 是 linux 的简单解决方案,但这个解决方案也适用于 OSX,所以 +1 这个脚本不等同于realpath
或 readlink -f
所做的。例如,它不适用于最后一个组件是符号链接的路径。
@josch:问题不在于符号链接解析。但是如果你想这样做,你可以为pwd
命令提供-P
选项:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
我喜欢这个答案,但它只有在允许用户 cd 进入目录的情况下才有效。这可能并不总是可行的。
我个人最喜欢的 :) 不幸的是,当我喂它时它失败了“。”和“..”作为相对路径。略微改进的版本:***.com/a/51264222/508355【参考方案4】:
使用:
find "$(pwd)"/ -type f
获取所有文件或
echo "$(pwd)/$line"
显示完整路径(如果相对路径很重要)
【讨论】:
【参考方案5】:对于它的价值,我投票支持所选择的答案,但想分享一个解决方案。缺点是,它仅适用于 Linux - 在堆栈溢出之前,我花了大约 5 分钟试图找到 OSX 等价物。不过我确定它就在那里。
在 Linux 上,您可以将 readlink -e
与 dirname
结合使用。
$(dirname $(readlink -e ../../../../etc/passwd))
产量
/etc/
然后你用dirname
的妹妹basename
来得到
文件名
$(basename ../../../../../passwd)
产量
passwd
把它们放在一起..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
产量
/etc/passwd
如果您以目录为目标,那么您是安全的,basename
不会返回任何内容
最终输出中会出现双斜线。
【讨论】:
dirname
、readlink
和 basename
的优秀作品。这帮助我获得了符号链接的绝对路径——而不是它的目标。
当你想返回符号链接的路径时不起作用(我恰好需要这样做......)。
你怎么能找到一个不存在的路径的绝对路径?
@synthesizerpatel 很容易,我想;如果我在/home/GKFX
中并输入touch newfile
,那么在我按下回车键之前,您可以知道我的意思是“创建/home/GKFX/newfile”,这是一个不存在的文件的绝对路径还没有。【参考方案6】:
我认为这是最便携的:
abspath()
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
如果路径不存在,它将失败。
【讨论】:
没有必要再cd回来。见***.com/a/21188136/1504556。恕我直言,您的答案是此页面上的最佳答案。对于那些感兴趣的人,该链接解释了为什么这个解决方案有效。 这不是很便携,dirname
是一个 GNU 核心实用程序,我相信并非所有 unixen 都通用。
@einpoklum dirname
是 POSIX 标准实用程序,请参见此处:pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
天哪,谢谢。我已经尝试修复使用 $1##*/
的版本一天了,现在我用 basename "$1"
替换了那个垃圾,它似乎终于可以正确处理以 / 结尾的路径了。
注意,对于以 ../..
结尾的路径,这并不正确【参考方案7】:
realpath
可能是最好的
但是……
最初的问题一开始就很困惑,举个例子很糟糕 与所述问题相关。
所选答案实际上回答了给出的示例,而不是完全 标题中的问题。第一个命令就是那个答案(是 真的吗 ?我怀疑),并且没有'/'也可以。我看不到 第二个命令在做什么。
几个问题混在一起:
将相对路径名更改为绝对路径名,无论它是什么
表示,可能什么都没有。
(通常,如果您发出诸如 touch foo/bar
之类的命令,则
路径名foo/bar
必须为您存在,并且可能用于
计算,在实际创建文件之前。)
可能有多个绝对路径名表示同一个文件 (或潜在文件),特别是因为符号链接(symlinks) 在路径上,但可能出于其他原因(设备可能是 以只读方式安装两次)。一个人可能想也可能不想解决 明确此类符号链接。
到达非符号链接的符号链接链的末尾 文件或名称。这可能会或可能不会产生绝对路径名, 取决于它是如何完成的。一个人可能想,也可能不想 将其解析为绝对路径名。
不带选项的命令readlink foo
仅在其
参数foo
是一个符号链接,答案就是那个值
符号链接。没有其他链接。答案可能是相对路径:
符号链接参数的值是什么。
但是,readlink
有适用于所有人的选项(-f -e 或 -m)
文件,并给出一个绝对路径名(没有符号链接的那个)
参数实际表示的文件。
这适用于任何不是符号链接的东西,尽管可能
希望使用绝对路径名而不解析中间
路径上的符号链接。这是通过命令realpath -s foo
在符号链接参数的情况下,readlink
及其选项将
再次解析参数绝对路径上的所有符号链接,但是
这还将包括可能遇到的所有符号链接
跟随参数值。如果你想要一个,你可能不想要那个
参数符号链接本身的绝对路径,而不是任何东西
它可能链接到。同样,如果foo
是符号链接,realpath -s foo
将
在不解析符号链接的情况下获得绝对路径,包括
作为参数给出。
如果没有 -s
选项,realpath
的作用与
readlink
,除了简单读取一个链接的值,还有几个
其他事情。我只是不清楚为什么readlink
有它的
选项,显然与
realpath
.
探索网络并没有多说,除了可能有 跨系统的一些变化。
结论:realpath
是最好用的命令,用的最多
灵活性,至少对于此处要求的用途而言。
【讨论】:
readlink
says 的手册页:“注意 realpath(1) 是用于规范化功能的首选命令。”
@jarno 很有趣。我的手册页确实这么说,但日期是 2021 年 4 月。知道何时包含该行吗?
commit 制作于 2017 年。
@jarno 谢谢。我应该学会搜索那种信息。好吧,很高兴有一个在 4 年后得到官方支持的答案:-)。太糟糕了,没有徽章。【参考方案8】:
我最喜欢的解决方案是@EugenKonkov 的the one,因为它并不意味着存在其他实用程序(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
【讨论】:
【参考方案9】:Eugen 的回答不太适合我,但确实如此:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
附注,您当前的工作目录不受影响。
【讨论】:
【参考方案10】:对@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/..
出现。
【讨论】:
【参考方案11】:恕我直言,最好的解决方案是在此处发布的解决方案:https://***.com/a/3373298/9724628。
它确实需要 python 才能工作,但它似乎涵盖了所有或大部分边缘情况,并且是非常便携的解决方案。
-
解析符号链接:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
-
或者没有它:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
【讨论】:
【参考方案12】:在find
的情况下,最简单的方法可能是只给出搜索的绝对路径,例如:
find /etc
find `pwd`/subdir_of_current_dir/ -type f
【讨论】:
【参考方案13】:如果您在不存在 realpath 且 readlink 无法打印绝对路径的 Mac OS X 上使用 bash,则您可以选择编写自己的版本打印它。 这是我的实现:
(纯 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
(纯 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
【讨论】:
【参考方案14】:类似于@ernest-a 的回答,但在不影响$OLDPWD
或定义新函数的情况下,您可以触发子shell (cd <path>; pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
【讨论】:
【参考方案15】:他们所说的,除了find $PWD
或(在 bash 中)find ~+
更方便一点。
【讨论】:
【参考方案16】:如果相对路径是目录路径,那么试试我的,应该是最好的:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
【讨论】:
此解决方案仅适用于 bash,另请参阅 ***.com/a/5193087/712014【参考方案17】:echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk ' split($0,array," "); for(i in array) system("cd "array[i]" && echo $PWD") '
【讨论】:
感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。【参考方案18】:您可以对任何相对路径 $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
当我们希望 /
成为我们查找/替换的字符串的一部分时,\
字符会否定它。
【讨论】:
请注意,我认为您可以只使用dirname
和basename
。类似dir=`dirname $line`; file=`basename $line`; line=`cd $dir; pwd`/$file
【参考方案19】:
我无法找到一个可以在 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"
【讨论】:
【参考方案20】:这是一个相当短的函数,可用于仅使用 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
错误。这恰好是它试图模仿的 GNU/Linux readlink -f
命令的确切语义。
在 bash/zsh 中,您还可以将数字列表压缩为 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"
【讨论】:
【参考方案21】:我发现Eugen Konkov's 答案是最好的,因为它不需要安装任何程序。但是,对于不存在的目录,它会失败。
我写了一个适用于不存在的目录的函数:
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
删除的所有内容,解决了目录不存在的问题。
【讨论】:
【参考方案22】:如果您想将包含相对路径的变量转换为绝对路径,这可行:
dir=`cd "$dir"`
"cd" 回显而不改变工作目录,因为这里是在一个子 shell 中执行的。
【讨论】:
在 bash-4.3-p46 上,这不起作用:当我运行dir=`cd ".."` && echo $dir
时,shell 会打印一个空行【参考方案23】:
这是所有其他解决方案的链式解决方案,例如,当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")" "$?"
【讨论】:
【参考方案24】:基于@EugenKonkov 的this answer 和@HashChange 的this answer,我的回答结合了前者的简洁性和后者的.
和..
的处理。我相信下面的所有选项都依赖于基本的Shell Command Language POSIX standards。
使用dirname
和basename
,一个选项是:
absPathDirname()
[ -d "$1" ] && set -- "$1" || set -- "`dirname "$1"`" "/`basename "$1"`"
echo "`cd "$1"; pwd`$2";
不使用dirname
或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"
【讨论】:
以上是关于如何检索给定相对路径的绝对路径的主要内容,如果未能解决你的问题,请参考以下文章
C# 中是不是存在一种方法来获取给定两个绝对路径输入的相对路径? [复制]
C程序只能在给定绝对路径时写入文件,而在给定相对路径时不能写入