如何在Mac上获得GNU的readlink -f的行为?


376

在Linux上,该readlink实用程序接受-f附加链接后面的选项。这似乎不适用于Mac和可能基于BSD的系统。等价的是什么?

以下是一些调试信息:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

1
有点晚了,但是您的问题没有提到您使用的shell。这很重要,因为readlink可以是内置命令或外部命令。
0xC0000022L 2012年

1
那也许可以解释差异。我很确定我两次都使用过bash。
troelskn 2012年

1
为什么此选项在Mac上是非法的?
CommaToast'9

3
我真的希望苹果能使OS X支持更多默认的Linux路径,并解决类似这样的问题。他们可以做到而不会破坏任何东西,不是吗?
CommaToast 2014年

1
@CommaToast好吧,它们与Perl一起提供,所以++ :-) ...打开Terminal.app并输入:touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile...就我而言,我看到:/ Users / cito / myfile`。将其添加到下面的我的回复中。干杯。
G. Cito

Answers:


181

readlink -f 做两件事:

  1. 沿着符号链接序列进行迭代,直到找到实际文件为止。
  2. 它返回该文件的规范名称,即其绝对路径名。

如果愿意,您可以仅构建使用原始readlink行为实现相同功能的Shell脚本。这是一个例子。显然,您可以将其插入您自己的脚本中,以在其中调用readlink -f

#!/bin/sh

TARGET_FILE=$1

cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`

# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
    TARGET_FILE=`readlink $TARGET_FILE`
    cd `dirname $TARGET_FILE`
    TARGET_FILE=`basename $TARGET_FILE`
done

# Compute the canonicalized name by finding the physical path 
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT

请注意,这不包括任何错误处理。特别重要的是,它不会检测符号链接周期。一种简单的方法是计算循环次数,如果碰到一个不可思议的数字(例如1,000),则失败的次数。

编辑使用pwd -P代替$PWD

请注意,此脚本需要被称为像./script_name filename,没有-f,更改$1$2,如果你希望能够与使用-f filename像GNU的readlink。


1
据我所知,如果路径的父目录是符号链接,那将行不通。例如。如果为foo -> /var/cux,则foo/bar不会被解析,因为bar虽然不是,但不是链接foo
troelskn

1
啊。是。它不是那么简单,但是您可以更新上面的脚本来解决这个问题。我将相应地编辑(实际上是重写)答案。
基思·史密斯,2009年

好吧,链接可能在路径中的任何地方。我猜脚本可以遍历路径的每个部分,但是那时确实变得有点复杂。
troelskn

1
谢谢。问题是$ PWD根据我们遵循的符号链接中的值为我们提供了逻辑上的工作目录。我们可以使用'pwd -P'来获得真实的物理目录。它应该通过将“ ..”追逐到文件系统的根目录来进行计算。我将在答案中相应地更新脚本。
基思·史密斯

12
很好的解决方案,但是它不能正确处理需要转义的路径,例如带有嵌入式空格的文件或目录名称。为了弥补这方面,使用cd "$(dirname "$TARGET_FILE")"TARGET_FILE=$(readlink "$TARGET_FILE")TARGET_FILE=$(basename "$TARGET_FILE")在上面的代码中适当的地方。
mklement0 2012年

438

MacPorts和Homebrew提供了包含(GNU readlink)的coreutils软件包greadlink。感谢mackb.com中的Michael Kallweitt帖子。

brew install coreutils

greadlink -f file.txt

如果要从bash以外的地方调用readlink,则另一个解决方案是删除/ usr / bin / readlink,然后创建到/ usr / local / bin / greadlink的链接。
chinglun 2014年

8
请不要,除非您a)为自己编写软件b)想要与他人搞混。以对任何人“都行得通”的方式编写它。
kgadek

14
@ching请在您的位置执行此操作.bashrcexport PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine

1
问题要求在OS X或BSD系统上使用等效功能,但这不能回答问题。您还必须安装Homebrew。除了GNU Readlink之外,您还将安装许多东西。如果您正在为标准OS X安装脚本,则此答案无济于事。您可以使用的组合pwd -Preadlink在OS X上,而无需安装任何东西
贾森小号

4
我希望 Mac上的GNU和其他实用程序能够很好地工作,没有前缀,并且可以从用户的上轻松访问PATH。上面的内容是否以开头brew install <package> --default-names。很多时候,在问询者的技术要求之外,但在类似情况下,本站点上的一个问题得到了轻微的回答,甚至更多,并且仍然很有帮助。 topbug.ne​​t/blog/2013/04/14/…–
Pysis

43

您可能对realpath(3)或Python 感兴趣os.path.realpath。两者并不完全相同。C库调用要求存在中间路径组件,而Python版本不存在。

$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r--  1 miles    wheel  0 Jul 11 21:08 a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 b -> a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a

我知道您说过您希望使用比其他脚本语言更轻量的东西,但是以防万一无法编译二进制文件,可以使用Python和ctypes(在Mac OS X 10.5上可用)包装库调用:

#!/usr/bin/python

import ctypes, sys

libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p

def realpath(path):
    buffer = ctypes.create_string_buffer(1024) # PATH_MAX
    if libc.realpath(path, buffer):
        return buffer.value
    else:
        errno = libc.__error().contents.value
        raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))

if __name__ == '__main__':
    print realpath(sys.argv[1])

具有讽刺意味的是,此脚本的C版本应该更短。:)


是的,realpath确实是我想要的。但是我不得不编译一个二进制文件才能从Shell脚本中获得此功能似乎很尴尬。
troelskn

4
那么为什么不在外壳脚本中使用Python一线式呢?(与单线呼叫没有什么不同,对readlink吗?)
Telemachus

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"可以使用类似的路径'~/.symlink'
Wes Turner

@troelskin我没有意识到“允许” Perl,Python !...在这种情况下,我要添加perl -MCwd=abs_path -le 'print abs_path readlink(shift);'我的答案:-)
G. Cito

27

我不愿意再使用另一种实现,但是我需要a)一个可移植的纯shell实现,以及b)单元测试的覆盖范围,因为类似这样的边缘情况的数量是不平凡的

有关测试和完整代码,请参见我在Github上的项目。以下是实现的概要:

正如Keith Smith指出的那样,readlink -f它做了两件事:1)递归地解决符号链接,以及2)对结果进行规范化,因此:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

首先,symlink解析器实现:

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac 
}

请注意,这是完整实现的稍微简化的版本。完整的实现对符号链接周期进行了少量检查,并对输出进行了一些调整。

最后,用于规范化路径的函数:

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}   

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

就是这样,或多或少。粘贴到脚本中很简单,但是却很棘手,以至于您会疯狂地依赖任何没有针对用例的单元测试的代码。


3
这不是local
POSIX-

这不等于readlink -f,也不是realpath。假设Mac上安装了coreutils公式(因此greadlink可用),请尝试使用ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..打印你的home目录(它解决了/tmp/linkeddir应用之前链接..父目录REF),但你的代码产生/private/tmp//tmp/linkeddir/..不是一个符号,但是-d /tmp/linkeddir/..是真实的,因此cd /tmp/linkeddir/..需要你/tmp,他们的典型路径/private/tmp。您的代码将执行realpath -L操作。
马丁·彼得斯

27

perl中的一个简单的一线工具,几乎可以在任何外部没有任何外部依赖的情况下工作:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

将取消引用符号链接。

脚本中的用法可能是这样的:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

4
要获得结尾的换行符,请添加-l,例如perl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar

26
  1. 安装自制软件
  2. 运行“ brew install coreutils”
  3. 运行“ greadlink -f path”

greadlink是实现-f的gnu readlink。您也可以使用macports或其他端口,我更喜欢自制软件。



如果需要使用这些命令的常规名称,则可以从bashrc将“ gnubin”目录添加到PATH中,例如:PATH =“ / usr / local / opt / coreutils / libexec / gnubin:$ PATH”
Denis Trofimov

20

我亲自编写了一个名为realpath的脚本,看起来像:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

sys.argv[1]如果希望脚本打印第一个参数的实路径,则应将其更改为。sys.argv[0]只是打印pyton脚本本身的真实路径,不是很有用。
雅各布·埃格

17
这是别名版本~/.profilealias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

很好的答案,和很棒的一线@jwhitlock。由于引用了OP readlink -f,因此这里是@jwhitlock别名的修改版本,以支持可能的-f标志: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codeniffer

11

那这个呢?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

这是唯一对我有效的解决方案。我需要解析路径../../../->/
keflavich 2012年

如果您有一个符号链接/usr/bin/(例如alternatives系统喜欢的符号链接),这将无法正常工作。
akostadinov

不适用于单个文件,仅适用于目录。GNU的readlink与目录,文件,符号链接等作品
codesniffer

7

这是一个可移植的shell函数,可以在任何 Bourne可比的shell中使用。它将解决相对路径标点“ ..或”。和取消引用符号链接。

如果由于某种原因您没有realpath(1)命令或readlink(1),则可以使用别名。

which realpath || alias realpath='real_path'

请享用:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in $1
  do
    # Resolve relative path punctuation.
    if [ "$I" = "." ] || [ -z "$I" ]
      then continue
    elif [ "$I" = ".." ]
      then FOO="${FOO%%/${FOO##*/}}"
           continue
      else FOO="${FOO}/${I}"
    fi

    ## Resolve symbolic links
    if [ -h "$FOO" ]
    then
    IFS=$OIFS
    set `ls -l "$FOO"`
    while shift ;
    do
      if [ "$1" = "->" ]
        then FOO=$2
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

同样,以防万一有人对此感兴趣的是如何在100%纯shell代码中实现basename和dirname:

## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
  echo "${1%/*}"
}

## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
  echo "${1##*/}"
}

您可以在我的Google网站上找到此Shell代码的更新版本:http : //sites.google.com/site/jdisnard/realpath

编辑:此代码是根据2句(freeBSD样式)许可的条款许可的。可以通过上面指向我的网站的超链接找到许可证的副本。


在Mac OS X Lion的bash shell中,real_path返回/ filename而不是/ path / to / filename。

@Barry在OSX Snow Leopard上也是如此。更糟糕的是,连续调用real_path()会连接输出!
Dominique

@Dominique:不足为奇,因为该函数的内部不是在自己的子外壳中运行的……确实丑陋而脆弱。
0xC0000022L 2012年

7

FreeBSDOSX具有stat从NetBSD派生的版本。

您可以使用格式开关调整输出(请参见上面链接的手册页)。

%  cd  /service
%  ls -tal 
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------  3 root wheel  8 Jun 30 13:59 .s6-svscan
drwxr-xr-x  3 root wheel  5 Jun 30 13:34 .
lrwxr-xr-x  1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x  1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R  clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y  clockspeed-adjust
/var/service/clockspeed-adjust

某些OS X版本stat 可能缺少-f%R格式选项。在这种情况下-stat -f%Y就足够了。该-f%Y选项将显示符号链接的目标,而-f%R显示与文件相对应的绝对路径名。

编辑:

如果您能够使用Perl(最新版​​本的装有Darwin / OS X perl),则:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

将工作。


2
这在FreeBSD 10上是正确的,但在OS X 10.9上则不是。
Zanchey 2014年

接得好。引用的统计手册页面适用于OS / X 10.9。缺少的R选项-f%。可能stat -f%Y会提供所需的输出。我会调整答案。请注意,该stat工具出现在4.10版的FreeBSD和1.6版的NetBSD中。
G. Cito 2014年

Perl命令在10.15上无法正常工作。在当前目录中给它一个文件,并在当前目录中给它一个文件(没有文件)。
codeniffer

7

解决此问题并在安装了Homebrew或FreeBSD的Mac上启用readlink功能的最简单方法是安装“ coreutils”软件包。在某些Linux发行版和其他POSIX OS上可能也有必要。

例如,在FreeBSD 11中,我通过调用以下命令进行安装:

# pkg install coreutils

在具有Homebrew的MacOS上,命令为:

$ brew install coreutils

不太确定为什么其他答案这么复杂,仅此而已。这些文件不在其他位置,只是尚未安装。


7
brew install coreutils --with-default-names更具体。否则,您最终将获得“ greadlink” ...
Henk

我看不到2010 stackoverflow.com/a/4031502/695671和2012 stackoverflow.com/a/9918368/695671的答案有什么不同。问题实际上是“ ...在Mac以及可能基于BSD的系统上。等价的是?” 我认为这不是一个很好的答案。原因很多,但您可能将Mac OS计算机作为目标,却无法安装任何东西。pwd -P非常简单,但是它在许多其他答案中却消失了,包括重复的答案,因此是低票。
杰森S

3

开始更新

这是一个经常发生的问题,我们将一个名为realpath-lib的Bash 4库免费使用(MIT许可证)。默认情况下,它旨在模拟readlink -f并包括两个测试套件,以验证(1)它适用于给定的unix系统,以及(2)如果安装了readlink -f,则进行验证(但这不是必需的)。此外,它还可以用于调查,识别和解开深陷的符号链接和循环引用,因此它可以作为诊断深度嵌套的物理或符号目录和文件问题的有用工具。可以在github.combitbucket.org上找到它。

结束更新

除了Bash之外,不依赖任何其他东西的另一个非常紧凑和高效的解决方案是:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

这还包括一个环境设置no_symlinks,该环境设置提供了解析到物理系统的符号链接的能力。只要no_symlinks设置为某项,即no_symlinks='on'符号链接,便会解析到物理系统。否则,将应用它们(默认设置)。

这应该在任何提供Bash的系统上都可以使用,并且将返回与Bash兼容的退出代码以进行测试。


3

已经有很多答案,但是没有一个对我有用...所以这就是我现在正在使用的答案。

readlink_f() {
  local target="$1"
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}

1
如果$target是路径,则此方法无效,仅当是文件时,此方法有效。
MaxGhost

3

对我有用的一种懒惰方式

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

我想迟到总比没有好。我之所以开发这种软件很特别,是因为我的Fedora脚本无法在Mac上运行。问题是依赖和Bash。Mac没有它们,或者如果有,它们通常位于其他地方(另一条路径)。跨平台Bash脚本中的依赖路径操作充其量是令人头疼的事,而最坏的情况是安全风险-因此,如果可能的话,最好避免使用它们。

下面的函数get_realpath()是简单的,以Bash为中心的函数,不需要依赖项。我仅使用Bash内置的echocd。它也是相当安全的,因为在此过程的每个阶段都对所有内容进行了测试,并返回错误条件。

如果您不想遵循符号链接,则将set -P放在脚本的前面,否则cd在默认情况下应解析符号链接。已使用{absolute | 相对| 符号链接| local},并返回文件的绝对路径。到目前为止,我们还没有遇到任何问题。

function get_realpath() {

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

您可以将其与其他函数get_dirname,get_filename,get_stemname和validate_path结合使用。这些可以在我们的GitHub存储库中找到为realpath-lib(完整披露-这是我们的产品,但我们免费提供给社区,没有任何限制)。它也可以用作指导工具-有据可查。

我们已经尽力应用所谓的“现代Bash”实践,但是Bash是一个很大的主题,我敢肯定,总会有改进的空间。它需要Bash 4+,但如果仍然存在,则可以与较旧的版本一起使用。


1

由于我的工作被非BSD Linux和macOS的人所使用,因此我选择在构建脚本中使用这些别名(sed因为存在类似问题,所以选择了这些别名):

##
# If you're running macOS, use homebrew to install greadlink/gsed first:
#   brew install coreutils
#
# Example use:
#   # Gets the directory of the currently running script
#   dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
#   alias al='pico ${dotfilesDir}/aliases.local'
##

function globalReadlink () {
  # Use greadlink if on macOS; otherwise use normal readlink
  if [[ $OSTYPE == darwin* ]]; then
    greadlink "$@"
  else
    readlink "$@"
  fi
}

function globalSed () {
  # Use gsed if on macOS; otherwise use normal sed
  if [[ $OSTYPE == darwin* ]]; then
    gsed "$@"
  else
    sed "$@"
  fi
}

您可以添加以自动安装homebrew + coreutils依赖项的可选检查:

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Install brew if needed
  if [ -z "$(which brew)" ]; then 
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; 
  fi
  # Check for coreutils
  if [ -z "$(brew ls coreutils)" ]; then
    brew install coreutils
  fi
fi

我想做到真正的“全球性”,需要检查其他人……但这可能接近80/20标记。


1

说明

coreutils是一个brew软件包,用于安装与Mac OSX实现相对应的GNU / Linux核心实用程序,以便您可以使用这些实用程序

您可能会在mac osx系统上找到与Linux coreutils(“ Core Utilities”)相似的程序或实用程序,但它们在某些方面有所不同(例如,具有不同的标志)。

这是因为这些工具的Mac OSX实现不同。要获得原始的类似GNU / Linux的行为,您可以coreutils通过brew软件包管理系统安装软件包。

这将安装相应的核心实用程序,前缀为g。例如readlink,您将找到一个相应的greadlink程序。

为了使readlink性能类似于GNU readlinkgreadlink)实现,可以在安装coreutils之后创建一个简单的别名。

实作

  1. 安装brew

按照https://brew.sh/上的说明进行操作

  1. 安装coreutils软件包

brew install coreutils

  1. 创建别名

您可以将别名放在〜/ .bashrc,〜/ .bash_profile或任何用于保留bash别名的位置。我个人将我的〜/ .bashrc

alias readlink=greadlink

您可以为其他coreutils创建类似的别名,例如gmv,gdu,gdf等。但是请注意,Mac机器上的GNU行为可能与过去使用本机coreutils的其他人混淆,或者可能在您的mac系统上以意外的方式出现。


0

为OS X编写了一个realpath实用程序,它可以提供与相同的结果readlink -f


这是一个例子:

(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11  8 25 19:29 a -> /etc/passwd

(jalcazar@mac tmp)$ realpath a
/etc/passwd


如果您使用的是MacPorts,则可以使用以下命令进行安装:sudo port selfupdate && sudo port install realpath


0

真正的平台独立型也将是这个R-onliner

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

要实际模拟readlink -f <path>,需要使用$ 2而不是$ 1。


默认情况下,除了R几乎不在任何系统上。尚未在Mac上(自10.15起)是此问题。
codeniffer

0

readlink -fPOSIX Shell脚本的POSIX兼容实现

https://github.com/ko1nksm/readlinkf

这是POSIX兼容的(没有bashism)。它既不使用readlink也不使用realpath。通过与GNU进行比较,我已经验证了它完全相同readlink -f(请参阅测试结果)。它具有错误处理和良好的性能。您可以安全地从替换readlink -f。许可证为CC0,因此您可以将其用于任何项目。

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  target=$1 max_symlinks=10 CDPATH=''

  [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
  [ -d "${target:-/}" ] && target="$target/"

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -gt 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      cd -P "${target%/*}/" 2>/dev/null || break
      target=${target##*/}
    fi

    if [ ! -L "$target" ]; then
      target="${PWD%/}${target:+/}$target"
      printf '%s\n' "${target:-/}"
      return 0
    fi

    # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
    #   <file mode>, <number of links>, <owner name>, <group name>,
    #   <size>, <date and time>, <pathname of link>, <contents of link>
    # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
    link=$(ls -dl "$target" 2>/dev/null) || break
    target=${link#*" $target -> "}
  done
  return 1
}

请参考最新代码。它可能有些固定。


-2

Perl具有readlink功能(例如,如何在Perl中复制符号链接?)。这适用于大多数平台,包括OS X:

perl -e "print readlink '/path/to/link'"

例如:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

5
这样做与readlink没有-f选项-没有递归,没有绝对路径-一样,因此您最好readlink直接使用。
mklement0

-2

@Keith Smith的回答给出了一个无限循环。

这是我的答案,我只在SunOS上使用(SunOS错过了很多POSIX和GNU命令)。

这是一个脚本文件,您必须将其放在$ PATH目录之一中:

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link="$1"
while [ -L "$link" ]; do
  lastLink="$link"
  link=$(/bin/ls -ldq "$link")
  link="${link##* -> }"
  link=$(realpath "$link")
  [ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done

echo "$link"


-5

在我的系统和您的系统之间,readlink的路径不同。请尝试指定完整路径:

/sw/sbin/readlink -f


1
/ sw / sbin和/ usr / bin之间的区别是什么?为什么两个二进制文件不同?
troelskn

4
啊哈..所以芬克包含了与readlinkgnu兼容的替代品。知道这很高兴,但这不能解决我的问题,因为我需要我的脚本在其他人的机器上运行,并且我不能要求他们为此安装fink。
troelskn

您没有回答原始问题。请重写您的答案。
Jaime Hablutzel
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.