如何在Shell脚本中解析符号链接

时间:2020-03-05 18:38:56  来源:igfitidea点击:

给定绝对路径或者相对路径(在类Unix系统中),我想在解决所有中间符号链接后确定目标的完整路径。同时解决〜username表示法的奖励点。

如果目标是目录,则可以将chdir()进入目录,然后调用getcwd(),但是我真的很想从shell脚本中执行此操作,而不是编写C帮助程序。不幸的是,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"。

解决方案

回答

根据标准,pwd -P应该返回符号链接已解析的路径。

来自unistd.h的C函数char * getcwd(char * buf,size_t size)应该具有相同的行为。

getcwd
密码

回答

即使将它们作为符号链接调用,常见的Shell脚本也常常必须找到其"主"目录。因此,脚本必须仅从$ 0中找到其"真实"位置。

cat `mvn`

在我的系统上会打印一个包含以下内容的脚本,这应该是我们所需要的一个很好的提示。

if [ -z "$M2_HOME" ] ; then
  ## resolve links - 
readlink -f "$path"
may be a link to maven's home PRG="
realpath $path
" # 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`

回答

readlink $path

编者注:上面的代码适用于GNUreadlink和FreeBSD / PC-BSD / OpenBSDreadlink,但不适用于OS X 10.11及更高版本。
GNUreadlink提供了其他相关选项,例如-m来解析符号链接,无论最终目标是否存在。

请注意,自GNU coreutils 8.15(2012-01-06)起,提供了一个Realpath程序,该程序比上面的程序更钝和更灵活。它也与同名的FreeBSD util兼容。它还包括在两个文件之间生成相对路径的功能。

realpath - return the canonicalized absolute pathname

realpath  expands  all  symbolic  links  and resolves references to '/./', '/../' and extra '/' characters in the null terminated string named by path and
       stores the canonicalized absolute pathname in the buffer of size PATH_MAX named by resolved_path.  The resulting path will have no symbolic link, '/./' or
       '/../' components.

[以下管理员补充来自halloleo danorton的评论]

对于Mac OS X(至少通过10.11.x版本),请使用不带-f选项的readlink

#!/bin/bash

# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "##代码##")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "##代码##")

# resolve symlinks
while [[ -h $SELF_PATH ]]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink "$SELF_PATH")
    SELF_PATH=$(cd "$DIR" && cd "$(dirname -- "$SYM")" && pwd)/$(basename -- "$SYM")
done

编者注:这不会递归解析符号链接,因此不会报告最终目标。例如,给定符号链接" a"指向" b",而符号链接又指向" c",则仅报告" b"(并且不会确保将其输出为绝对路径)。
在OS X上使用以下perl命令来填补缺少的readlink -f功能的空白:
perl -MCwd -le'打印Cwd :: abs_path(shift)'" $ path"`

回答

我的最爱之一是" realpath foo"

##代码##

回答

如果只需要目录," pwd -P"似乎可以工作,但是如果出于某些原因想要实际可执行文件的名称,我认为这无济于事。这是我的解决方案:

##代码##