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

下面是一些调试信息:

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

当前回答

真正的平台独立也是r -online

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

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

其他回答

MacPorts和Homebrew提供了包含greadlink (GNU readlink)的coreutils包。这要归功于Michael Kallweitt在mackb.com上的帖子。

brew install coreutils

greadlink -f file.txt

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

由于使用非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的标记。

您可能既需要可移植的纯shell实现,也需要单元测试覆盖率,因为此类情况的边缘情况的数量非常多。

查看我的项目在Github上的测试和完整代码。下面是实现的概要:

正如Keith Smith敏锐地指出的那样,readlink -f做了两件事:1)递归地解析符号链接,2)规范化结果,因此:

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

差不多就是这样。简单到可以粘贴到您的脚本中,但也非常棘手,如果您依赖于任何用例中没有单元测试的代码,那就太疯狂了。

这是一个便携式壳函数,应该在任何Bourne可比壳工作。 它将解决相对路径标点符号”..或者。”以及取消对符号链接的引用。

如果由于某种原因,您没有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##*/}"
}

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

编辑: 此代码是根据2-clause (freeBSD风格)许可的条款授权的。 从上面的超链接到我的网站,可以找到许可证的副本。