在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。

其他回答

Echo $(cd $(dirname file1);pwd - p)

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

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

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

这是一个便携式壳函数,应该在任何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风格)许可的条款授权的。 从上面的超链接到我的网站,可以找到许可证的副本。

您可能既需要可移植的纯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")
}

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

POSIX兼容的readlink -f实现POSIX shell脚本

https://github.com/ko1nksm/readlinkf

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

此代码用于bat -core项目。

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  max_symlinks=40
  CDPATH='' # to avoid changing to an unexpected directory

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

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      case $target in
        /*) cd -P "${target%/*}/" 2>/dev/null || break ;;
        *) cd -P "./${target%/*}" 2>/dev/null || break ;;
      esac
      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
}

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