无需符号链接即可将相对路径转换为绝对路径


Answers:


79

您可以使用该readlink实用程序,并提供以下-f选项:

-f, --canonicalize

      canonicalize  by  following  every symlink in every component of
      the given name recursively; all  but  the  last  component  must
      exist

某些发行版,例如使用GNU coreutilsFreeBSD的发行版,还附带了一个realpath(1)实用程序,该实用程序基本上只调用realpath(3)并执行几乎相同的操作。


16
-f开关在Mac OS X上不存在(至少从10.8.5开始)。如果没有该-f开关,它将仅返回错误代码。
iconoclast

3
某些系统既不附带readlink也不提供realpath,尤其是Solaris和HP-UX。
Sildoreth

对于Mac问题:brew install coreutils apple.stackexchange.com/a/69332/23040,如果您不想执行将所有标准Mac CLI工具重定向到新安装的GNU等效项的步骤,请致电greadlink
阿德里安

18

可移植地,该PWD变量由外壳程序设置为当前目录的一个绝对位置。该路径的任何组件都可以是符号链接。

case $f in
  /*) absolute=$f;;
  *) absolute=$PWD/$f;;
esac

如果你想消除...也,切换到该目录包含文件,并获得$PWD有:

if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}

没有跟随符号链接的便携式方法。如果您有目录的路径,那么大多数unices会$(cd -- "$dir" && pwd -P 2>/dev/null || pwd)提供不使用符号链接的路径,因为跟踪符号链接的shell易于实现pwd -P(“ P”表示“物理”)。

某些unices提供实用程序来打印文件的“物理”路径。

  • 合理地讲,最近的Linux系统(具有GNU coreutils或BusyBox)具有readlink -fFreeBSD≥8.3,NetBSD≥4.0和OpenBSD早于2.2。
  • FreeBSD≥4.3拥有realpath(它在某些Linux系统上也存在,并且在BusyBox中)。
  • 如果有Perl,则可以使用Cwd模块

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file

为什么将管道插入pwd$(cd -- "$dir" && pwd -P 2>/dev/null | pwd))?似乎并不在乎stdin。
Mateusz Piotrowski

1
@MateuszPiotrowski Typo,我想写||
Gilles


3

alisterrss67本文中介绍最稳定,兼容和最简单的方法。我从未见过比这更好的方法。

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`

如果您想返回原始位置,

ORGPATH=`pwd`
RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd $ORGPATH

要么

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd -

我希望这会有所帮助。这对我来说是最大的解决方案。


12
这实际上不是一个好方法。从目录中切换回去是不可靠的:您可能没有返回权限,或者在此期间目录可能已重命名。而是在子shell中更改目录:ABSPATH=$(cd -- "$RELPATH" && pwd)。请注意替换处的双引号(以便在路径包含空格和通配符的--情况下不会引起折断)和(在路径以开头的情况下-)。同样,这仅适用于目录,并且不能按要求规范化符号链接。请参阅我的答案以获取更多详细信息。
Gilles 2012年

“>您可能没有返回权限”。您的意思是cd到目录,但不能cd回来?您能否提供一些示例方案。
Talespin_Kit

0

这里的其他答案很好,但不足以满足我的需求。我需要一个可以在任何计算机上的脚本中使用的解决方案。我的解决方案是编写一个Shell脚本,可以从需要的脚本中调用它。

#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
  printf 'Usage: respath path [working-directory]\n' >&2
  exit 1
fi
cantGoUp=

path=$1
if [ $# -gt 1 ]; then
  cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks

#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
  printf '%s\n' "$path"
  exit 0
fi

#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
  case "$path" in
    ..*)
      if [ "$cwd" = '/' ]; then
        printf 'Invalid relative path\n' >&2
        exit 1
      fi
      if [ "$path" = '..' ]; then
        path=
      else
        path=`echo "$path" | sed 's;^\.\./;;'`
      fi
      cwd=`dirname $cwd`
      ;;
    *)
      break
      ;;
  esac
done

cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
  if [ -z "$cwd" ]; then
    cwd='/'
  fi
  printf '%s\n' "$cwd"
else
  printf '%s/%s\n' "$cwd" "$path"
fi

此解决方案是为Bourne Shell编写的,以实现可移植性。我在名为“ respath.sh”的脚本中拥有此文件。

该脚本可以这样使用:

respath.sh '/path/to/file'

或者像这样

respath.sh '/relative/path/here' '/path/to/resolve/relative/to'

该脚本使用第二个自变量的路径作为起点来解析第一个自变量的路径。如果仅提供第一个参数,则脚本将解析相对于当前工作目录的路径。


请注意,您现在可能只会在Solaris 10和更早的版本中找到Bourne shell。 Bourne Shell中最喜欢的Bourne shell的实现,因为SVR2(1984年)已经pwd内置,不支持-P(Bourne shell的未实施该逻辑$ PWD处理像POSIX壳做)。
斯特凡Chazelas

如果您放弃了Bourne兼容性,则可以使用POSIX $ {var ## pattern}语法,避免那些会破坏某些值的echo | sed。
斯特凡Chazelas

您忘记检查cd(和pwd)的退出状态。你或许应该使用cd -P --以及万一第一个参数包含了一些..
斯特凡Chazelas

您的case支票应..|../*)改为..*
斯特凡Chazelas

有缺失报价的一个案例dirname $cwd
斯特凡Chazelas

0

另外,如果您有一些$1(通常是${PWD})的预定义路径,则可以使用以下路径:

CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
    case $1 in
        /*) echo "CWD=${CWD}"; ;;
        *) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
    esac
fi

0

尊重其他所有人的回答,我发现下面的功能在linux / MAC OSX之间比在任何地方都可以找到的功能更加容易和可移植。可能会帮助某人。

#!/usr/bin/bash
get_absolute_path(){
    file_path=`pwd $1`
    echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path

-1

假设变量a存储路径名(a =“ whatever”)

1.对于潜在的空间路径:

c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi

2.对于实际问题,即将路径转换为绝对值:readlink可以检索符号链接的内容,可以按以下方式使用。(注意readlink -f本来可以是完美的,但是至少在Mac OS X bash中不可用,其中-f代表FORMATS。)

if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi

3.刚学到pwd -Pman pwd:-P是为“ 显示物理当前工作目录(所有符号链接解决) ”,因此

if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi; a=$(cd $a; pwd -P)

也会工作。

结论:12结合起来可以满足您的两个需求,而包含空格的路径名已经在更简洁的3中得到了考虑。


你可能错了。例如,如果您的路径中包含空格,则上述大多数功能均无效。
Anthon

或有时是全球字符。并且它不能按要求解析符号链接。
dave_thompson_085 '17

我已经删除了有关包含空格的路径名称的“评论”响应,并相应地修改了“答案”部分。
vxtal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.