如何在 Mac 上获取 GNU readlink -f 的行为?
题
在 Linux 上, readlink
实用程序接受一个选项 -f
跟随附加链接。这似乎不适用于 Mac 以及可能基于 BSD 的系统。相当于什么?
这是一些调试信息:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
解决方案
readlink -f
做两件事:
- 它沿循环的序列链接,直到找到一个实际的文件。
- 它返回该文件的 规范化 名称--即其absolute pathname.
如果你想,你可以建立一个脚本用的香草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人。
编辑要用的 pwd -P
而不是的 $PWD
.
注意,这个脚本希望被称为喜欢 ./script_name filename
, ,没有 -f
, ,改变 $1
要 $2
如果你想要能够使用 -f filename
如GNU readlink.
其他提示
和MacPorts的自制提供一个的coreutils 含包greadlink
(GNU的readlink)。信用迈克尔Kallweitt后在mackb.com。
brew install coreutils
greadlink -f file.txt
您可能有兴趣在 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版本应该是短的。 :)
我讨厌桩上又一实施方式中,但我需要一个)的一种便携式,纯壳实施下,和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")
}
就是这样,更多或更少。够简单粘贴到你的脚本,但不够刁钻,你一定是疯了依靠在没有单元测试你的用例的任何代码。
- 安装自制
- 运行"泡安装下载管理器,各种文件下载"
- 运行"greadlink-f的道路"
greadlink是gnu readlink,实现了-f。你可以使用macports或其他人,我喜欢自制.
一个简单的一行在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)"
我做一个真实路径亲自脚本看起来有点像:
#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
这个是什么?
function readlink() {
DIR="${1%/*}"
(cd "$DIR" && echo "$(pwd -P)")
}
自由BSD 和 操作系统X 有一个版本 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(Darwin/OS X 附带安装了最新版本的 Perl) perl
) 然后:
perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt
将工作。
下面是一种便携式壳函数应工作在 ANY 伯恩类似壳。 这既解决了相对路径标点“..或”。和解除引用符号链接。
如果出于某种原因,不具有真实路径(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%纯壳代码基本名和目录名:
## 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##*/}"
}
的http://网站您可以在我的谷歌网站查找更新的这个shell代码版本。 google.com/site/jdisnard/realpath
编辑: 此代码下的2子句(FreeBSD的风格)许可证的条款许可。 许可证的副本可以通过上述超级链接以下到我的网站上找到。
要解决这个问题并在Mac启动的readlink的功能瓦特/自制安装或FreeBSD是安装“的coreutils”包的最简单方法。也可能是某些Linux发行版和其它POSIX OS必要的。
例如,在FreeBSD的11,I安装通过调用:
# pkg install coreutils
在用自制的MacOS,命令将是:
$ brew install coreutils
不知道为什么其他的答案是如此的复杂,这一切就是这么简单。这些文件不会在不同的地方,他们只是没有安装呢。
开始更新
这是一个非常常见的问题,因此我们整理了一个免费使用的 Bash 4 库(MIT 许可证),名为 真实路径库. 。这是为了模拟 读取链接-f 默认情况下,包括两个测试套件来验证 (1) 它是否适用于给定的 UNIX 系统以及 (2) 读取链接-f 如果已安装(但这不是必需的)。此外,它还可用于调查、识别和展开深层、损坏的符号链接和循环引用,因此它可以成为诊断深层嵌套的物理或符号目录和文件问题的有用工具。可以在以下位置找到: github.com 或者 bitbucket.org.
结束更新
另一个非常紧凑且高效的解决方案,除了 Bash 之外不依赖任何东西:
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 的任何系统,并将返回 Bash 兼容的退出代码以用于测试目的。
现在已经有很多答案,但没有为我工作......这就是我现在使用的东西。
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"
}
良药苦口,我想。我是出于因为我的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+,但可以作出与旧版本的工作,如果他们仍然存在。
忠实地与平台indpendent也将是此R-onliner
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
要实际模仿readlink -f <path>
,$ 2,而不是$ 1将需要被使用。
由于我的工作是由人使用与非BSD Linux以及mac os,我已经选择使用这些化名在我们建立脚本(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
}
任选检查你可以增加自动安装 自制 + 下载管理器,各种文件下载 相关性:
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标志。
我写为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
解释
下载管理器,各种文件下载的是一个 brew
包,安装GNU/Linux的核心工具,这对应于Mac OS x执行它们,以便可以使用的那些
你可以找到方案或多数水利on mac os x系统,似乎类似于Linux下载管理器,各种文件下载("核心工具")然而,它们的区别在某些方面(例如具有不同的标志)。
这是因为Mac OS x执行这些工具是不同的。得到原始GNU/Linux样的行为可以安装的 coreutils
通过包 brew
包管理系统。
这将安装相应的核心事业,作为前缀 g
.E.g。对于 readlink
, 你将找到一对应 greadlink
程序。
为了使 readlink
执行似GNU readlink
(greadlink
)执行情况,你可以做一个简单的别名之后安装下载管理器,各种文件下载.
执行情况
- 安装泡
按照说明在 https://brew.sh/
- 安装的下载管理器,各种文件下载软件包
brew install coreutils
- 创建一个别名
你可以把你的别名在~/.更改,~/.bash_profile,或无论你是用来保持你的庆典别名。我个人保持矿~/.更改
alias readlink=greadlink
你可以创建类似的别名,其他下载管理器,各种文件下载,例如gmv,gdu,gdf,等等。但要注意的是GNU行为mac机可能会令人困惑,对其他人使用与地下载管理器,各种文件下载,或者可能会出现在意想不到的方式在你的mac系统。
这是我使用:
stat -f %N $your_path
要的readlink的路径是我的系统和你之间不同。请尝试指定的完整路径:
/sw/sbin/readlink -f
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
这@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"