macos 如何在 Mac 上获得 GNU 的 readlink -f 的行为?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1055671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I get the behavior of GNU's readlink -f on a Mac?
提问by troelskn
On Linux, the readlink
utility accepts an option -f
that follows additional links. This doesn't seem to work on Mac and possibly BSD based systems. What would the equivalent be?
在 Linux 上,该readlink
实用程序接受一个-f
跟随附加链接的选项。这似乎不适用于 Mac 和基于 BSD 的系统。等价物是什么?
Here's some debug information:
以下是一些调试信息:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
采纳答案by Keith Smith
readlink -f
does two things:
readlink -f
做两件事:
- It iterates along a sequence of symlinks until it finds an actual file.
- It returns that file's canonicalizedname—i.e., its absolute pathname.
- 它沿着一系列符号链接迭代,直到找到一个实际的文件。
- 它返回该文件的规范化名称——即它的绝对路径名。
If you want to, you can just build a shell script that uses vanilla readlink behavior to achieve the same thing. Here's an example. Obviously you could insert this in your own script where you'd like to call readlink -f
如果您愿意,您可以构建一个 shell 脚本,该脚本使用 vanilla readlink 行为来实现相同的目的。这是一个例子。显然,您可以将其插入到您想要调用的脚本中readlink -f
#!/bin/sh
TARGET_FILE=
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
Note that this doesn't include any error handling. Of particular importance, it doesn't detect symlink cycles. A simple way to do this would be to count the number of times you go around the loop and fail if you hit an improbably large number, such as 1,000.
请注意,这不包括任何错误处理。特别重要的是,它不会检测符号链接循环。一个简单的方法是计算你绕过循环的次数,如果你遇到一个不可能的大数字,比如 1,000,就会失败。
EDITED to use pwd -P
instead of $PWD
.
编辑使用pwd -P
而不是$PWD
.
Note that this script expects to be called like ./script_name filename
, no -f
, change $1
to $2
if you want to be able to use with -f filename
like GNU readlink.
请注意,如果您希望能够像 GNU readlink 一样使用,则此脚本应被称为 like ./script_name filename
, no -f
,更改$1
为。$2
-f filename
回答by tomyjwu
MacPorts and Homebrew provide a coreutilspackage containing greadlink
(GNU readlink). Credit to Michael Kallweitt post in mackb.com.
MacPorts 和 Homebrew 提供了一个包含(GNU readlink)的coreutils包greadlink
。感谢 Michael Kallweitt 在 mackb.com 上的帖子。
brew install coreutils
greadlink -f file.txt
回答by Miles
You may be interested in realpath(3)
, or Python's os.path.realpath
. The two aren't exactly the same; the C library call requires that intermediary path components exist, while the Python version does not.
您可能对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
I know you said you'd prefer something more lightweight than another scripting language, but just in case compiling a binary is insufferable, you can use Python and ctypes (available on Mac OS X 10.5) to wrap the library call:
我知道你说过你更喜欢比另一种脚本语言更轻量级的语言,但为了防止编译二进制文件无法忍受,你可以使用 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])
Ironically, the C version of this script ought to be shorter. :)
具有讽刺意味的是,这个脚本的 C 版本应该更短。:)
回答by Michael Kropat
I hate to pile on with yet another implementation, but I needed a) a portable, pure shell implementation, and b) unit-test coverage, as the number of edge-cases for something like this are non-trivial.
我不想继续使用另一个实现,但我需要 a)一个可移植的纯 shell 实现,以及 b)单元测试覆盖率,因为像这样的事情的边缘情况的数量是非常重要的。
See my project on Githubfor tests and full code. What follows is a synopsis of the implementation:
有关测试和完整代码,请参阅我在 Github 上的项目。下面是实现的概要:
As Keith Smith astutely points out, readlink -f
does two things: 1) resolves symlinks recursively, and 2) canonicalizes the result, hence:
正如 Keith Smith 敏锐地指出的那样,readlink -f
做了两件事:1) 递归地解析符号链接,以及 2) 规范化结果,因此:
realpath() {
canonicalize_path "$(resolve_symlinks "")"
}
First, the symlink resolver implementation:
首先,符号链接解析器实现:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf '%s\n' ""
fi
}
_prepend_path_if_relative() {
case "" in
/* ) printf '%s\n' "" ;;
* ) printf '%s\n' "/" ;;
esac
}
Note that this is a slightly simplified version of the full implementation. The full implementation adds a small check for symlink cycles, as well as massages the output a bit.
请注意,这是完整实现的略微简化版本。完整的实现添加了一个小的符号链接周期检查,并稍微调整了输出。
Finally, the function for canonicalizing a path:
最后,规范化路径的函数:
canonicalize_path() {
if [ -d "" ]; then
_canonicalize_dir_path ""
else
_canonicalize_file_path ""
fi
}
_canonicalize_dir_path() {
(cd "" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "")
file=$(basename -- "")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
That's it, more or less. Simple enough to paste into your script, but tricky enough that you'd be crazy to rely on any code that doesn't have unit tests for your use cases.
就是这样,或多或少。简单到可以粘贴到您的脚本中,但又足够棘手,以至于您会疯狂地依赖任何没有针对您的用例进行单元测试的代码。
回答by JinnKo
A simple one-liner in perl that's sure to work almost everywhere without any external dependencies:
perl 中的一个简单的单行代码,它几乎可以在任何地方工作,无需任何外部依赖:
perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file
Will dereference symlinks.
将取消引用符号链接。
Usage in a script could be like this:
脚本中的用法可能是这样的:
readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "";}
ABSPATH="$(readlinkf ./non-absolute/file)"
回答by Andrew
- Install homebrew
- Run "brew install coreutils"
- Run "greadlink -f path"
- 安装自制软件
- 运行“brew install coreutils”
- 运行“greadlink -f 路径”
greadlink is the gnu readlink that implements -f. You can use macports or others as well, I prefer homebrew.
greadlink 是实现 -f 的 gnu readlink。您也可以使用 macports 或其他,我更喜欢自制软件。
回答by James
I made a script called realpath personally which looks a little something like:
我个人制作了一个名为 realpath 的脚本,它看起来有点像:
#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
回答by Peeech
What about this?
那这个呢?
function readlink() {
DIR="${1%/*}"
(cd "$DIR" && echo "$(pwd -P)")
}
回答by masta
Here is a portable shell function that should work in ANYBourne comparable shell. It will resolve the relative path punctuation ".. or ." and dereference symbolic links.
这是一个可移植的 shell 函数,它应该可以在任何Bourne 类似的 shell 中工作。它将解析相对路径标点符号“.. 或 .” 并取消引用符号链接。
If for some reason you do not have a realpath(1) command, or readlink(1) this can be aliased.
如果由于某种原因您没有 realpath(1) 命令或 readlink(1) 这可以是别名。
which realpath || alias realpath='real_path'
Enjoy:
享受:
real_path () {
OIFS=$IFS
IFS='/'
for I in
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 [ "" = "->" ]
then FOO=
shift $#
break
fi
done
IFS='/'
fi
done
IFS=$OIFS
echo "$FOO"
}
also, just in case anybody is interested here is how to implement basename and dirname in 100% pure shell code:
此外,以防万一有人对这里感兴趣的是如何在 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##*/}"
}
You can find updated version of this shell code at my google site: http://sites.google.com/site/jdisnard/realpath
您可以在我的 google 站点上找到此 shell 代码的更新版本:http: //sites.google.com/site/jdisnard/realpath
EDIT: This code is licensed under the terms of the 2-clause (freeBSD style) license. A copy of the license may be found by following the above hyperlink to my site.
编辑:此代码根据 2-clause(freeBSD 风格)许可条款获得许可。可以通过上面指向我的站点的超链接找到许可证的副本。
回答by G. Cito
FreeBSDand OSXhave a version of stat
derived from NetBSD.
FreeBSD和OSX有一个stat
从 NetBSD 派生的版本。
You can adjust the output with format switches (see the manual pages at the links above).
您可以使用格式开关调整输出(请参阅上面链接中的手册页)。
% 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
Some OS X versions of stat
may lack the -f%R
option for formats. In this case -stat -f%Y
may suffice. The -f%Y
option will show the target of a symlink, whereas -f%R
shows the absolute pathname corresponding to the file.
某些 OS X 版本stat
可能缺少-f%R
格式选项。在这种情况下-stat -f%Y
可能就足够了。该-f%Y
选项将显示符号链接的目标,而-f%R
显示与文件对应的绝对路径名。
EDIT:
编辑:
If you're able to use Perl (Darwin/OS X comes installed with recent verions of perl
) then:
如果您能够使用 Perl(Darwin/OS X 安装了最新版本的perl
),那么:
perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt
will work.
将工作。