将相对路径转换为绝对路径?
Posted
技术标签:
【中文标题】将相对路径转换为绝对路径?【英文标题】:Converting relative path into absolute path? 【发布时间】:2011-05-02 00:13:18 【问题描述】:我不确定这些路径是否重复。给定相对路径,如何使用 shell 脚本确定绝对路径?
例子:
relative path: /x/y/../../a/b/z/../c/d
absolute path: /a/b/c/d
【问题讨论】:
您的“相对”路径实际上是绝对路径,但不是规范形式。相对路径永远不会以/
开头。也许搜索“规范路径”。
bash/fish command to print absolute path to a file 的可能重复项
【参考方案1】:
来自this source:
#!/bin/bash
# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.
ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"
你也可以做一个 Perl 单行,例如使用Cwd::abs_path
【讨论】:
非常好。如果还给出了文件名怎么办。比如/x/y/../../a/b/z/../c/d.txt => /a/b/c/d.txt 第一个在包含空格的路径上失败(尽管有趣的是,像foo; echo BAR
这样的路径并没有像我预期的那样行为不端)。 -1 引起你的注意...
@j_random_hacker - (1) 已修复; (2) 用户收到新cmets的通知,无需通过投票来获得关注:)
+2 :) 我知道,“引起你的注意”是狡猾的词——“供应动机”更像是……我现在把它作为一般政策来做,因为我已经决定了不幸的是,结果好坏参半。
有趣的是,如果指定的路径不存在,Cwd::abs_path
会失败。【参考方案2】:
我在unix中遇到的最可靠的方法是readlink -f
:
$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d
几个注意事项:
-
这也有解决所有符号链接的副作用。这可能是可取的,也可能不是可取的,但通常是这样的。
如果您引用不存在的目录,
readlink
将给出空白结果。如果要支持不存在的路径,请改用readlink -m
。不幸的是,这个选项在 ~2005 年之前发布的 readlink 版本中不存在。
【讨论】:
readlink -f is not available on OS X. @jared-beck 你能证明readlink -f
做同样的事情吗?在我的(OS X El Capitan)安装中,readlink -f
首先基本上是stat -f
,这表明:-f Display information using the specified format. See the FORMATS section for a description of valid formats.
。也许你安装了coreutils
?
目前,OS X El Capitan 有一个不同的 readlink 命令,可以找到符号链接的真实路径,而不是将相对路径解析为绝对路径。因此,如果我们想要可移植,看起来我们只能更改目录然后返回。【参考方案3】:
看看'realpath'。
$ realpath
usage: realpath [-q] path [...]
$ realpath ../../../../../
/data/home
【讨论】:
【参考方案4】:这可能会有所帮助:
$path = "~user/dir/../file"
$resolvedPath = glob($path); # (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path
$absPath = abs_path($path);
【讨论】:
【参考方案5】:使用bash
# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"
# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "$relative_file%/*" && pwd )"/"$relative_file##*/"
$relative_file%/*
与 dirname "$relative_file"
的结果相同
$relative_file##*/
与 basename "$relative_file"
的结果相同
注意事项:不解析符号链接(即不规范化路径)=>如果您使用符号链接,可能无法区分所有重复项。
使用realpath
命令realpath
完成这项工作。另一种方法是使用readlink -e
(或readlink -f
)。但是 realpath
默认情况下并不经常安装。如果您不能确定 realpath
或 readlink
是否存在,您可以使用 perl 替换它(见下文)。
使用perl
如果realpath
在您的系统中不可用,Steven Kramer 会建议一个 shell 别名:
$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file
或者如果您更喜欢直接使用 perl:
$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file
这个单行 perl 命令使用Cwd::realpath
。实际上有三个 perl 函数。它们采用单个参数并返回绝对路径名。以下详细信息来自文档Perl5 > Core modules > Cwd。
abs_path()
使用与getcwd()
相同的算法。符号链接和相对路径组件(.
和 ..
)被解析为返回规范路径名,就像 realpath
。
use Cwd 'abs_path';
my $abs_path = abs_path($file);
realpath()
是abs_path()
的同义词
use Cwd 'realpath';
my $abs_path = realpath($file);
fast_abs_path()
是abs_path()
的一个更危险但可能更快的版本
use Cwd 'fast_abs_path';
my $abs_path = fast_abs_path($file);
这些函数仅在请求时导出 => 因此使用Cwd
来避免arielf 指出的“未定义子例程” 错误。如果要导入所有这三个函数,可以使用单个 use Cwd
行:
use Cwd qw(abs_path realpath fast_abs_path);
【讨论】:
+1,但您能否通过在示例前添加use Cwd qw(abs_path realpath fast_abs_path);
来使其更完整?照原样,所有 3 个示例都会产生“未定义的子例程”错误。
具有不错的 Perl 但没有安装 realpath
的发行版的单行版本:alias realpath="perl -MCwd -e 'print Cwd::realpath ($ARGV[0]), qq<\n>'"
@olibre MacOSX,Perl v5.18.2。脚本地址:gist.github.com/thefosk/43296cb8bf74497c9d69
@Mark 你的shell 在别名的定义中扩展$ARGV
,所以Perl sees Cwd::realpath ([0])
,这是一个数组引用。在别名中转义 $ARGV
。 zsh没有这个问题。
查看我的编辑,它转义了别名,应该适用于 bash/zsh/ksh。【参考方案6】:
由于这些年来我遇到过很多次,而这一次我需要一个可以在 OSX 和 linux 上使用的纯 bash 便携版本,所以我继续写了一个:
活版在这里:
https://github.com/keen99/shell-functions/tree/master/resolve_path
但是为了 SO,这里是当前版本(我觉得它已经过很好的测试......但我愿意接受反馈!)
让它适用于普通的 bourne shell (sh) 可能并不难,但我没有尝试......我太喜欢 $FUNCNAME 了。 :)
#!/bin/bash
resolve_path()
#I'm bash only, please!
# usage: resolve_path <a file or directory>
# follows symlinks and relative paths, returns a full real path
#
local owd="$PWD"
#echo "$FUNCNAME for $1" >&2
local opath="$1"
local npath=""
local obase=$(basename "$opath")
local odir=$(dirname "$opath")
if [[ -L "$opath" ]]
then
#it's a link.
#file or directory, we want to cd into it's dir
cd $odir
#then extract where the link points.
npath=$(readlink "$obase")
#have to -L BEFORE we -f, because -f includes -L :(
if [[ -L $npath ]]
then
#the link points to another symlink, so go follow that.
resolve_path "$npath"
#and finish out early, we're done.
return $?
#done
elif [[ -f $npath ]]
#the link points to a file.
then
#get the dir for the new file
nbase=$(basename $npath)
npath=$(dirname $npath)
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -d $npath ]]
then
#the link points to a directory.
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
echo "opath [[ $opath ]]" >&2
echo "npath [[ $npath ]]" >&2
return 1
fi
else
if ! [[ -e "$opath" ]]
then
echo "$FUNCNAME: $opath: No such file or directory" >&2
return 1
#and break early
elif [[ -d "$opath" ]]
then
cd "$opath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -f "$opath" ]]
then
cd $odir
ndir=$(pwd -P)
nbase=$(basename "$opath")
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
echo "opath [[ $opath ]]" >&2
return 1
fi
fi
#now assemble our output
echo -n "$ndir"
if [[ "x$nbase:=" != "x" ]]
then
echo "/$nbase"
else
echo
fi
#now return to where we were
cd "$owd"
return $retval
这是一个经典的例子,感谢 brew:
%% ls -l `which mvn`
lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn
使用这个函数,它会返回-real-路径:
%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`
%% test.sh
relative symlinked path:
/usr/local/bin/mvn
and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
【讨论】:
【参考方案7】:我想使用realpath
,但它在我的系统 (macOS) 上不可用,所以我想出了这个脚本:
#!/bin/sh
# NAME
# absolute_path.sh -- convert relative path into absolute path
#
# SYNOPSYS
# absolute_path.sh ../relative/path/to/file
echo "$(cd $(dirname $1); pwd)/$(basename $1)"
例子:
./absolute_path.sh ../styles/academy-of-management-review.csl
/Users/doej/GitHub/styles/academy-of-management-review.csl
【讨论】:
以上是关于将相对路径转换为绝对路径?的主要内容,如果未能解决你的问题,请参考以下文章