给定一个绝对路径或相对路径(在类unix系统中),我希望在解析任何中间符号链接之后确定目标的完整路径。同时还可以解析~用户名符号。

如果目标是一个目录,则可以将chdir()放入该目录,然后调用getcwd(),但我确实希望从shell脚本而不是编写C helper来完成此操作。不幸的是,shell倾向于试图向用户隐藏符号链接的存在(这是OS X上的bash):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

我想要的是一个函数resolve(),这样当从上面的例子中的tmp目录执行时,resolve("foo") == "/Users/greg/tmp/bar"。


当前回答

为了解决Mac不兼容的问题,我想到了

echo `php -r "echo realpath('foo');"`

不是很好,但是跨越了操作系统

其他回答

function realpath {
    local r=$1; local t=$(readlink $r)
    while [ $t ]; do
        r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
        t=$(readlink $r)
    done
    echo $r
}

#example usage
SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..

普通shell脚本通常必须找到它们的“主”目录,即使它们是作为符号链接调用的。因此脚本必须从$0开始找到它们的“真实”位置。

cat `mvn`

在我的系统上输出一个包含以下内容的脚本,这应该能很好地提示您需要什么。

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home
  PRG="$0"

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
      PRG="$link"
    else
      PRG="`dirname "$PRG"`/$link"
    fi
  done

  saveddir=`pwd`

  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`

我的答案在这里Bash:如何获得一个符号链接的真正路径?

但简而言之,在脚本中非常方便:

script_home=$( dirname $(realpath "$0") )
echo Original script home: $script_home

这些是GNU coreutils的一部分,适合在Linux系统中使用。

为了测试一切,我们把symlink放到/home/test2/,修改一些额外的东西,并从根目录运行/调用它:

/$ /home/test2/symlink
/home/test
Original script home: /home/test

在哪里

Original script is: /home/test/realscript.sh
Called script is: /home/test2/symlink

我的2分钱。这个函数是POSIX兼容的,源和目标都可以包含->。但是,我还没有让它与容器换行符或制表符的文件名一起工作,因为ls通常有这些问题。

resolve_symlink() {
  test -L "$1" && ls -l "$1" | awk -v SYMLINK="$1" '{ SL=(SYMLINK)" -> "; i=index($0, SL); s=substr($0, i+length(SL)); print s }'
}

我认为这里的解决方案是file命令,使用一个自定义魔术文件,只输出所提供的符号链接的目标。

您的路径是目录还是文件?如果它是一个目录,它很简单:

(cd "$DIR"; pwd -P)

然而,如果它可能是一个文件,那么这将不起作用:

DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"

因为符号链接可能解析为相对路径或完整路径。

在脚本上,我需要找到真正的路径,这样我就可以引用配置或其他脚本安装在一起,我使用这个:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done

您可以将SOURCE设置为任何文件路径。基本上,只要路径是符号链接,它就解析那个符号链接。诀窍在循环的最后一行。如果解析的符号链接是绝对的,它将使用它作为SOURCE。然而,如果它是相对的,它将在DIR之前为它,通过我首先描述的简单技巧将其解析为一个真实位置。