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

tro*_*skn 341 macos freebsd sh

在Linux上,该readlink实用程序接受一个-f跟随其他链接的选项.这似乎不适用于Mac和可能基于BSD的系统.相当于什么?

这是一些调试信息:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
Run Code Online (Sandbox Code Playgroud)

tom*_*jwu 411

MacPorts和Homebrew提供了一个包含(GNU readlink)的coreutilsgreadlink.感谢Michael Kallweitt在mackb.com上的帖子.

brew install coreutils

greadlink -f file.txt
Run Code Online (Sandbox Code Playgroud)

  • 同样适用于Homebrew; coreutils提供`greadlink`. (12认同)
  • @ching在你的`.bashrc`中执行此操作:`export PATH ="/ usr/local/opt/coreutils/libexec/gnubin:$ PATH"` (12认同)
  • 请不要,除非你a)为自己编写软件b)想要与别人混淆.以这样的方式写它,它"对任何人都有用". (8认同)
  • 该问题询问 OS X 或 BSD 系统上的等效项,这并没有回答这个问题。您还必须安装 Homebrew。除了 GNU Readlink 之外,您还将安装许多东西。如果您正在为标准 OS X 安装编写脚本,那么这个答案将没有帮助。您可以在 OS X 上结合使用 `pwd -P` 和 `readlink`,无需安装任何东西 (4认同)
  • **我想要我的Mac上的GNU和其他工具我相信这样做很好,没有前缀,并且可以从我的用户的`PATH`轻松访问.以上是从'brew install <package> --default-names`开始的.很多时候,这个网站上的问题已经得到了一些问题的回答,或者更多,在提问者的技术要求之外,但在类似的情况下,并且仍然非常有帮助.https://www.topbug.ne​​t/blog/2013/04/14/install-and-use-gnu-command-line-tools-in-mac-os-x/ (4认同)

Kei*_*ith 164

readlink -f 做两件事:

  1. 它沿着一系列符号链接迭代,直到找到实际文件.
  2. 它返回该文件的规范化名称 - 即其绝对路径名.

如果您愿意,您可以构建一个使用vanilla 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
Run Code Online (Sandbox Code Playgroud)

请注意,这不包括任何错误处理.特别重要的是,它不会检测符号链接周期.一个简单的方法是计算你循环的次数,如果你遇到一个不可思议的大数字,例如1000,就会失败.

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

请注意,如果您希望能够像GNU readlink一样使用此脚本,则需要调用like ./script_name filename,no -f,change $1to .$2-f filename

  • 很好的解决方案,但它不能正确处理需要转义_的_path,例如带有_embedded spaces_的文件或目录名.为了弥补这方面,使用`CD "$(目录名称 "$ TARGET_FILE")"`和`TARGET_FILE = $(的readlink "$ TARGET_FILE")`和`TARGET_FILE = $(名前缀 "$ TARGET_FILE")`在适当的地方上面的代码. (11认同)

Mil*_*les 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
Run Code Online (Sandbox Code Playgroud)

我知道你说你喜欢比其他脚本语言更轻量级的东西,但是为了编译二进制文件是令人难以忍受的,你可以使用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])
Run Code Online (Sandbox Code Playgroud)

具有讽刺意味的是,这个脚本的C版本应该更短.:)

  • 为什么不在shell脚本中使用Python one-liner呢?(与单行调用`readlink`本身没什么不同,是吗?) (4认同)
  • `python -c"import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv [1])))""$ {1}"`适用于`'〜/等路径. symlink'` (2认同)

Mic*_*pat 26

我讨厌继续使用另一个实现,但我需要a)一个可移植的纯shell实现,以及b)单元测试覆盖,因为像这样的边缘情况的数量是非平凡的.

在Github上查看我的项目以获取测试和完整代码.以下是实施的概要:

正如基思史密斯精明地指出的那样,readlink -f有两件事:1)递归地解析符号链接,以及2)规范化结果,因此:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}
Run Code Online (Sandbox Code Playgroud)

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

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 
}
Run Code Online (Sandbox Code Playgroud)

请注意,这是完整实现的略微简化版本.完整的实现为符号链接周期添加了一个小的检查,并稍微按摩了输出.

最后,规范化路径的功能:

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")
}
Run Code Online (Sandbox Code Playgroud)

就是这样,或多或少.很简单,可以粘贴到您的脚本中,但是非常棘手,您可能会依赖任何没有针对您的用例进行单元测试的代码.

  • 这不是POSIX - 它使用非POSIX`local`关键字 (3认同)

小智 24

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

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

  • 我看不出与 2010 年 /sf/answers/282205171/ 给出的答案有什么不同 (4认同)

Jin*_*nKo 21

perl中的一个简单的单行程,几乎可以在任何地方工作,没有任何外部依赖:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file
Run Code Online (Sandbox Code Playgroud)

将解除引用符号链接.

脚本中的用法可能如下所示:

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

  • 要获得尾随换行符,请添加 `-l`,例如 `perl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file` (6认同)

Jam*_*mes 18

我制作了一个名为realpath的脚本,看起来有点像:

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

  • 这是别名版本,对于`〜/ .profile`:`alias realpath ="python -c'import os,sys; print os.path.realpath(sys.argv [1])'"` (16认同)

Pee*_*ech 11

那这个呢?

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

  • 不适用于单个文件,仅适用于目录。GNU readlink 适用于目录、文件、符号链接等。 (2认同)

G. *_*ito 8

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
Run Code Online (Sandbox Code Playgroud)

某些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
Run Code Online (Sandbox Code Playgroud)

将工作.

  • 这在FreeBSD 10上是正确的,但在OS X 10.9上则不然. (2认同)

mas*_*sta 7

这是一个便携式shell函数,应该可以在任何 Bourne类似的shell中使用.它将解决相对路径标点符号"..或".和解除引用的符号链接.

如果由于某种原因你没有realpath(1)命令或readlink(1)这可能是别名.

which realpath || alias realpath='real_path'
Run Code Online (Sandbox Code Playgroud)

请享用:

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"
}
Run Code Online (Sandbox Code Playgroud)

另外,以防任何人对此感兴趣的是如何在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##*/}"
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:此代码根据2子句(freeBSD样式)许可条款获得许可.可以通过以上链接到我的网站找到许可证的副本.


Iza*_*ana 7

一种对我有用的懒惰方式,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink
Run Code Online (Sandbox Code Playgroud)

  • 不再滚动:这是 macOS 上最简洁、最好的答案。 (2认同)

Ave*_*man 6

解决这个问题并在Mac上安装Homebrew或FreeBSD时启用readlink功能的最简单方法是安装'coreutils'软件包.在某些Linux发行版和其他POSIX OS上也可能是必需的.

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

# pkg install coreutils

在使用Homebrew的MacOS上,命令将是:

$ brew install coreutils

不确定为什么其他答案如此复杂,这就是它的全部内容.这些文件不在同一个地方,它们还没有安装.

  • `brew install coreutils --with-default-names`更具体.或者你最终会得到"greadlink"...... (6认同)

yos*_*row 6

执行

  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 系统上以意想不到的方式运行。

解释

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

您可能会在 mac osx 系统上发现程序或实用程序,它们看起来与 Linux coreutils(“核心实用程序”)相似,但它们在某些方面有所不同(例如具有不同的标志)。

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

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

为了readlink像 GNU readlink( greadlink) 实现一样执行,您可以在安装 coreutils 后创建一个简单的别名。