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

其他回答

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
}

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

这个呢?

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

解决这个问题并在安装了Homebrew或FreeBSD的Mac上启用readlink功能的最简单方法是安装“coreutils”包。在某些Linux发行版和其他POSIX操作系统上可能也是必需的。

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

# PKG安装coreutils

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

$ brew install coreutils

不太确定为什么其他答案如此复杂,这就是它的全部。文件并没有在不同的地方,只是还没有安装。

Readlink -f做两件事:

它沿着符号链接序列迭代,直到找到一个实际的文件。 它返回该文件的规范化名称。,它的绝对路径名。

如果您愿意,您可以构建一个shell脚本,使用普通readlink行为来实现相同的功能。举个例子。显然,你可以在自己的脚本中插入这个,你想调用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),就会失败。

edit使用pwd -P而不是$ pwd。

注意,这个脚本希望被称为。/script_name文件名,不使用-f,如果你想能够像GNU readlink一样使用-f文件名,将$1更改为$2。

我想,迟到总比不到好。我之所以特别开发它,是因为我的Fedora脚本无法在Mac上运行。问题在于依赖关系和Bash。mac没有,或者如果有,它们通常在其他地方(另一条路径)。跨平台Bash脚本中的依赖路径操作在最好的情况下是令人头痛的,在最坏的情况下是存在安全风险的——因此最好尽可能避免使用它们。

下面的get_realpath()函数很简单,以bash为中心,不需要依赖关系。我只使用Bash内置的echo和cd。它也相当安全,因为在每个阶段都会测试所有内容,并返回错误条件。

如果您不想遵循符号链接,那么在脚本前面放置set -P,否则cd将默认解析符号链接。它已经用文件参数{absolute | relative | symlink | 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+,但如果旧版本仍然存在,则可以使其与旧版本一起工作。