我怎样才能得到GNU的的readlink -f的Mac上的行为?我怎样才能得到GNU的的readli

2019-05-14 01:17发布

在Linux上, readlink实用程序接受的选项-f后面附加的链接。 这似乎并没有在Mac和BSD可能根据系统的工作。 什么将相当于是什么?

这里的一些调试信息:

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

Answer 1:

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

请注意,这不包括任何错误处理。 特别重要的是,它不检测符号链接循环。 一个简单的方法来做到这将是算你周围的循环去的次数和失败,如果你打一个不大可能大量的,如1000。

编辑以使用pwd -P ,而不是$PWD

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



Answer 2:

MacPorts的和自制提供含有coreutils软件包greadlink (GNU的readlink)。 感谢迈克尔Kallweitt后在mackb.com。

brew install coreutils

greadlink -f file.txt


Answer 3:

您可能感兴趣的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版应该更短。 :)



Answer 4:

我讨厌桩上又一实现,但我需要一个) 的便携,纯壳实施 ,和b) 单元测试覆盖范围 ,作为边缘的情况下这样的事情的数量是不平凡的

见我在Github上的项目进行测试和完整的代码。 接下来是执行的摘要:

作为基思史密斯机敏地指出, readlink -f做两两件事:1)解析符号链接递归,和2)canonicalizes的结果,因此:

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

首先,符号链接解析器实现:

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")
}

就这样,或多或少。 够简单粘贴到你的脚本,但不够刁钻,你一定是疯了依靠不具备为您的使用情况下的单元测试的代码。



Answer 5:

  1. 安装自制软件
  2. 运行“酿造安装的coreutils”
  3. 运行“greadlink -f路径”

greadlink是实现-f在GNU的readlink。 您可以使用的MacPorts或加上其他的,我更喜欢自制。



Answer 6:

一个简单的一行在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)"


Answer 7:

我做一个真实路径亲自脚本看起来有点像:

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


Answer 8:

那这个呢?

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


Answer 9:

FreeBSD的和OSX有一个版本的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(达尔文/ OS X系统安装了最新的verions perl ),则:

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

将工作。



Answer 10:

这里是一个便携式外壳功能应该在任何伯恩类似壳工作。 这既解决了相对路径标点“..或”。 并取消引用符号链接。

如果由于某种原因你没有一个真实路径(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代码基本名称和目录名称:

## 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##*/}"
}

:你可以在我的谷歌网站上找到这个shell代码的更新版本http://sites.google.com/site/jdisnard/realpath

编辑:此代码下的2子句(FreeBSD的风格)许可证的条款许可。 该许可证的副本可以按照上面的超链接到我的网站上找到。



Answer 11:

最简单的方法来解决这个问题,并在Mac上启用的readlink的功能瓦特/安装自制软件或FreeBSD是安装“的coreutils”包。 也可能是某些Linux发行版和其它POSIX OS必要的。

例如,在FreeBSD下11,我安装通过调用:

# pkg install coreutils

在与自制的MacOS,该命令是:

$ brew install coreutils

真的不知道为什么其他的答案是如此的复杂,这一切就是这么简单。 该文件不是在不同的地方,他们只是还没有安装。



Answer 12:

开始更新

这是我们已经把一击4库免费使用(MIT许可证) 的真实路径称为-lib的这样一个常见的问题。 这样的设计是在默认情况下效仿的readlink -f,包括两个测试套件来验证(1),如果安装了它的工作原理对于一个给定的UNIX系统和(2)针对的readlink -f(但不是必需的)。 此外,它可以用来进行调查,鉴定和放松深,断符号链接和循环引用,所以它可以用于诊断深度嵌套的物理或象征性的目录和文件问题的一个有用工具。 它可以被发现在github.com或bitbucket.org 。

结束时更新

不依赖于任何东西,但猛砸另一个非常紧凑,高效的解决方案是:

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的任何系统上工作,并会返回一个猛砸兼容退出代码用于测试目的。



Answer 13:

现在已经有很多答案,但没有为我工作......这就是我现在使用的东西。

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"
}


Answer 14:

迟到总比不到好,我想。 我是出于因为我的Fedora脚本并没有在Mac上工作,专门制定本。 问题是依赖和Bash。 苹果电脑没有他们,或者如果他们这样做,他们往往是在其他地方(另一个路径)。 在跨平台的bash脚本依赖路径操作是最好的一个头痛和最坏安全风险 - 因此,最好避免使用它们,如果可能的话。

下面的函数get_realpath()是简单,击为中心,并且不需要依赖关系。 我只使用猛砸内建回声CD。 这也是相当安全的,因为一切都被在路上每个阶段测试,它会返回错误条件。

如果你不想跟随符号连接,然后将设置-P在脚本的前面,否则CD默认情况下应解决的符号链接。 当地}并返回到文件的绝对路径。 到目前为止,我们已经没有与此有关的任何问题。

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库为找到真实路径,LIB (充分披露-这是我们的产品,但我们提供它免费向社区没有任何限制)。 它也可以作为教学工具 - 这是有据可查的。

我们已经尽了最大申请所谓的“现代猛砸”的做法,但Bash是一个很大的问题,我敢肯定总会有改进的余地。 它需要猛砸4+,但可以作出与旧版本的工作,如果他们仍然存在。



Answer 15:

忠实地平台indpendent也将是这个R-onliner

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

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



Answer 16:

由于我的工作是通过使用非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
}

可选的检查,你可以添加到自动安装自制软件 + 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大关。



Answer 17:

我写了OS X的真实路径一个工具 ,可以提供相同的结果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



Answer 18:

这是我使用:

stat -f %N $your_path



Answer 19:

到的readlink的路径是我的系统和你之间的不同。 请尝试指定的完整路径:

/sw/sbin/readlink -f



Answer 20:

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


Answer 21:

从@Keith史密斯的答案给出了一个无限循环。

这里是我的回答,我在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"


文章来源: How can I get the behavior of GNU's readlink -f on a Mac?
标签: macos freebsd sh