如何在 Mac 上获取 GNU readlink -f 的行为?

发布于 2024-07-26 05:07:21 字数 1825 浏览 10 评论 0 原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(26

故笙诉离歌 2024-08-02 05:07:22

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

Perl has a readlink function (e.g. How do I copy symbolic links in Perl?). This works across most platforms, including OS X:

perl -e "print readlink '/path/to/link'"

For example:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c
我们只是彼此的过ke 2024-08-02 05:07:22

@Keith Smith 的答案给出了无限循环。

这是我的答案,我只在 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"

The answer from @Keith Smith gives an infinite loop.

Here is my answer, which i use only on SunOS (SunOS miss so much POSIX and GNU commands).

It's a script file you have to put in one of your $PATH directories:

#!/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"
有木有妳兜一样 2024-08-02 05:07:22

这就是我使用的:

stat -f %N $your_path

This is what I use:

stat -f %N $your_path

轮廓§ 2024-08-02 05:07:22

我的系统和你的系统之间的 readlink 路径不同。 请尝试指定完整路径:

<代码>/sw/sbin/readlink -f

The paths to readlink are different between my system and yours. Please try specifying the full path:

/sw/sbin/readlink -f

风向决定发型 2024-08-02 05:07:21

MacPorts 和 Homebrew 提供了一个包含 greadlink (GNU readlink) 的 coreutils 软件包。 感谢 Michael Kallweitt 在 mackb.com 上的帖子。

brew install coreutils

greadlink -f file.txt

MacPorts and Homebrew provide a coreutils package containing greadlink (GNU readlink). Credit to Michael Kallweitt post in mackb.com.

brew install coreutils

greadlink -f file.txt
春风十里 2024-08-02 05:07:21

readlink -f 做了两件事:

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

如果您愿意,您可以构建一个使用普通 readlink 行为的 shell 脚本来实现相同的功能。 这是一个例子。 显然,您可以将其插入到您想要调用 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

请注意,这不包括任何错误处理。 特别重要的是,它不检测符号链接循环。 执行此操作的一个简单方法是计算循环的次数,如果达到不可能的大数字(例如 1,000),则失败。

编辑为使用 pwd -P 而不是 $PWD

请注意,此脚本期望以 ./script_name filename 的方式调用,而不是 -f,将 $1 更改为 $2如果您希望能够像 GNU readlink 一样使用 -f filename

readlink -f does two things:

  1. It iterates along a sequence of symlinks until it finds an actual file.
  2. It returns that file's canonicalized name—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

#!/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

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.

EDITED to use pwd -P instead of $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.

茶底世界 2024-08-02 05:07:21

您可能对realpath(3)<感兴趣/code>,或 Python 的 os.路径.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该脚本的版本应该更短。 :)

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.

$ 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:

#!/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. :)

就此别过 2024-08-02 05:07:21

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

A simple one-liner in perl that's sure to work almost everywhere without any external dependencies:

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' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"
厌倦 2024-08-02 05:07:21

您可能需要可移植的纯 shell 实现单元测试覆盖率,因为此类情况的边缘情况数量非常重要< /em>.

请参阅我在 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")
}

或多或少就是这样。 足够简单,可以粘贴到您的脚本中,但又足够棘手,以至于您会疯狂地依赖任何没有针对您的用例的单元测试的代码。

You might need both a portable, pure shell implementation, and unit-test coverage, as the number of edge-cases for something like this is non-trivial.

See my project on Github for tests and full code. What follows is a synopsis of the implementation:

As Keith Smith astutely points out, readlink -f does two things: 1) resolves symlinks recursively, and 2) canonicalizes the result, hence:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

First, the symlink resolver implementation:

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 
}

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

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.

我的奇迹 2024-08-02 05:07:21
  1. 安装 homebrew
  2. 运行“brew install coreutils”
  3. 运行“greadlink -f path”

greadlink 是实现 -f 的 gnu readlink。 你也可以使用 macports 或其他,我更喜欢 homebrew。

  1. Install homebrew
  2. Run "brew install coreutils"
  3. Run "greadlink -f path"

greadlink is the gnu readlink that implements -f. You can use macports or others as well, I prefer homebrew.

偏爱自由 2024-08-02 05:07:21

我亲自制作了一个名为 realpath 的脚本,它看起来有点像:

#!/usr/bin/env python
import os, sys
print(os.path.realpath(sys.argv[1]))

I made a script called realpath personally which looks a little something like:

#!/usr/bin/env python
import os, sys
print(os.path.realpath(sys.argv[1]))
肩上的翅膀 2024-08-02 05:07:21

那这个呢?

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

What about this?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}
一张白纸 2024-08-02 05:07:21

一种对我有用的懒惰方法,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

A lazy way that works for me,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink
软甜啾 2024-08-02 05:07:21

实施

  1. 安装brew

按照https://brew.sh/ 中的说明

  1. 安装 coreutils 软件包

brew install coreutils

  1. 创建别名或符号链接

3a。 创建别名(每个用户)

您可以将别名放置在 ~/.bashrc、~/.bash_profile 或任何您习惯保存 bash 别名的位置。 我个人将其保存在 ~/.bashrc

alias readlink=greadlink

3b 中。 创建符号链接(系统范围)

ln -s /usr/local/bin/greadlink /usr/local/bin/readlink (来源:Izana)

这将在 /usr/local 中创建符号链接/bin,同时保持原始 readlink 二进制文件完好无损。 它之所以有效,是因为搜索 readlink 将返回 2 个结果。 但 /usr/local/bin 中的第二个优先。

例如 which readlink

要撤消此更改,只需 unlink /usr/local/bin/readlink

其他工具

您可以为其他 coreutils 创建类似的别名或符号链接如gmv、gdu、gdf等。 但请注意,Mac 计算机上的 GNU 行为可能会让习惯使用本机 coreutils 的其他人感到困惑,或者可能在您的 Mac 系统上以意想不到的方式运行。

说明

coreutils 是一个 brew 软件包,它安装与 Mac OSX 实现相对应的 GNU/Linux 核心实用程序,以便您可以使用这些

程序或实用程序。 mac osx 系统看起来与 Linux coreutils(“核心实用程序”)相似,但它们在某些方面有所不同(例如具有不同的标志)。

这是因为这些工具的 Mac OSX 实现是不同的。 要获得原始的类似 GNU/Linux 的行为,您可以通过 brew 包管理系统安装 coreutils 包。

这将安装相应的核心实用程序,前缀为 g。 例如对于readlink,您会找到相应的greadlink程序。

为了使 readlink 的性能与 GNU readlink (greadlink) 实现类似,您可以在安装 coreutils 后创建一个简单的别名或符号链接。

Implementation

  1. Install brew

Follow the instructions at https://brew.sh/

  1. Install the coreutils package

brew install coreutils

  1. Create an Alias or Symlink

3a. Create an an alias (per user)

You can place your alias in ~/.bashrc, ~/.bash_profile, or wherever you are used to keeping your bash aliases. I personally keep mine in ~/.bashrc

alias readlink=greadlink

3b. Create a symbolic link (system wide)

ln -s /usr/local/bin/greadlink /usr/local/bin/readlink (credit: Izana)

This will create a symbolic link in /usr/local/bin while keeping the original readlink binary in tact. It works because the search for readlink will return 2 results. But the second in /usr/local/bin will take precedence.

e.g. which readlink

To undo this change simply unlink /usr/local/bin/readlink

Additional Tools

You can create similar aliases or symlinks for other coreutils such as gmv, gdu, gdf, and so on. But beware that the GNU behavior on a mac machine may be confusing to others used to working with native coreutils, or may behave in unexpected ways on your mac system.

Explanation

coreutils is a brew package that installs GNU/Linux core utilities which correspond to the Mac OSX implementation of them so that you can use those

You may find programs or utilties on your mac osx system which seem similar to Linux coreutils ("Core Utilities") yet they differ in some ways (such as having different flags).

This is because the Mac OSX implementation of these tools are different. To get the original GNU/Linux-like behavior you can install the coreutils package via the brew package management system.

This will install corresponding core utilities, prefixed by g. E.g. for readlink, you will find a corresponding greadlink program.

In order to make readlink perform like the GNU readlink (greadlink) implementation, you can create a simple alias or symbolic link after you install coreutils.

许你一世情深 2024-08-02 05:07:21

FreeBSDOSX 有一个源自 NetBSD 的 stat 版本。

您可以使用格式开关调整输出(请参阅上面链接中的手册页)。

%  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 -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

就可以了。

FreeBSD and OSX have a version of statderived from 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.

EDIT:

If you're able to use Perl (Darwin/OS X comes installed with recent verions of perl) then:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

will work.

邮友 2024-08-02 05:07:21

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

例如,在 FreeBSD 11 中,我通过调用以下命令进行安装:

# pkg install coreutils

在使用 Homebrew 的 MacOS 上,命令为:

$brew install coreutils

不太确定为什么另一个答案是如此复杂,仅此而已。 这些文件并没有位于不同的位置,它们只是还没有安装。

The easiest way to solve this problem and enable the functionality of readlink on Mac w/ Homebrew installed or FreeBSD is to install 'coreutils' package. May also be necessary on certain Linux distributions and other POSIX OS.

For example, in FreeBSD 11, I installed by invoking:

# pkg install coreutils

On MacOS with Homebrew, the command would be:

$ brew install coreutils

Not really sure why the other answers are so complicated, that's all there is to it. The files aren't in a different place, they're just not installed yet.

丢了幸福的猪 2024-08-02 05:07:21

这是一个可移植的 shell 函数,应该可以在任何 Bourne 类似的 shell 中工作。
它将解析相对路径标点符号“.. 或 .” 并取消引用符号链接。

如果由于某种原因您没有 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 代码中实现基本名称和目录名:

## 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##*/}"
}

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

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

Here is a portable shell function that should work in ANY Bourne comparable shell.
It will resolve the relative path punctuation ".. or ." and dereference symbolic links.

If for some reason you do not have a realpath(1) command, or readlink(1) this can be aliased.

which realpath || alias realpath='real_path'

Enjoy:

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

also, just in case anybody is interested here is how to implement basename and dirname in 100% pure shell code:

## 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

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.

各自安好 2024-08-02 05:07:21

echo $(cd $(目录名 file1) ; pwd -P)

echo $(cd $(dirname file1) ; pwd -P)

尬尬 2024-08-02 05:07:21

开始更新

这是一个非常常见的问题,因此我们整理了一个名为 realpath-lib 的免费使用的 Bash 4 库(MIT 许可证)。 默认情况下,它旨在模拟 readlink -f,并包含两个测试套件来验证 (1) 它是否适用于给定的 UNIX 系统,以及 (2) 针对 readlink -f如果已安装(但这不是必需的)。 此外,它还可用于调查、识别和展开深层、损坏的符号链接和循环引用,因此它可以成为诊断深层嵌套的物理或符号目录和文件问题的有用工具。 可以在 github.combitbucket.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 兼容的退出代码以用于测试目的。

Begin Update

This is such a frequent problem that we have put together a Bash 4 library for free use (MIT License) called realpath-lib. This is designed to emulate readlink -f by default and includes two test suites to verify (1) that it works for a given unix system and (2) against readlink -f if installed (but this is not required). Additionally, it can be used to investigate, identify and unwind deep, broken symlinks and circular references, so it can be a useful tool for diagnosing deeply-nested physical or symbolic directory and file problems. It can be found at github.com or bitbucket.org.

End Update

Another very compact and efficient solution that does not rely on anything but Bash is:

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

}

This also includes an environment setting no_symlinks that provides the ability to resolve symlinks to the physical system. As long as no_symlinks is set to something, ie no_symlinks='on' then symlinks will be resolved to the physical system. Otherwise they will be applied (the default setting).

This should work on any system that provides Bash, and will return a Bash compatible exit code for testing purposes.

花辞树 2024-08-02 05:07:21

已经有很多答案,但没有一个对我有用......所以这就是我现在正在使用的。

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

There are already a lot of answers, but none worked for me... So this is what I'm using now.

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"
}
回梦 2024-08-02 05:07:21

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

Since my work is used by people with non-BSD Linux as well as macOS, I've opted for using these aliases in our build scripts (sed included since it has similar issues):

##
# 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
}

Optional check you could add to automatically install homebrew + coreutils dependencies:

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

I suppose to be truly "global" it needs to check others...but that probably comes close to the 80/20 mark.

杀お生予夺 2024-08-02 05:07:21

符合 POSIX 标准的 readlink -f POSIX shell 脚本实现

https: //github.com/ko1nksm/readlinkf

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

此代码在bats-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
}

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

POSIX compliant readlink -f implementation for POSIX shell scripts

https://github.com/ko1nksm/readlinkf

This is POSIX compliant (no bashism). It uses neither readlink nor realpath. I have verified that it is exactly the same by comparing with GNU readlink -f (see test results). It has error handling and good performance. You can safely replace from readlink -f. The license is CC0, so you can use it for any project.

This code is adopted in the bats-core project.

# 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
}

Please refer to the latest code. It may some fixed.

哥,最终变帅啦 2024-08-02 05:07:21

我想,迟到总比不到好。 我之所以专门开发这个是因为我的 Fedora 脚本无法在 Mac 上运行。 问题在于依赖关系和 Bash。 Mac 没有它们,或者即使有,它们通常在其他地方(另一条路径)。 跨平台 Bash 脚本中的依赖路径操作往好了说是令人头疼,往坏了说是安全风险 - 因此,如果可能的话,最好避免使用它们。

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

如果您不想跟踪符号链接,请将 set -P 放在脚本的前面,否则 cd 应该默认解析符号链接。 它已经使用 {absolute | } 的文件参数进行了测试。 相对 | 符号链接 | 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+,但可以与旧版本一起使用(如果它们仍然存在)。

Better late than never, I suppose. I was motivated to develop this specifically because my Fedora scripts weren't working on the Mac. The problem is dependencies and Bash. Macs don't have them, or if they do, they are often somewhere else (another path). Dependency path manipulation in a cross-platform Bash script is a headache at best and a security risk at worst - so it's best to avoid their use, if possible.

The function get_realpath() below is simple, Bash-centric, and no dependencies are required. I uses only the Bash builtins echo and cd. It is also fairly secure, as everything gets tested at each stage of the way and it returns error conditions.

If you don't want to follow symlinks, then put set -P at the front of the script, but otherwise cd should resolve the symlinks by default. It's been tested with file arguments that are {absolute | relative | symlink | local} and it returns the absolute path to the file. So far we've not had any problems with it.

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

}

You can combine this with other functions get_dirname, get_filename, get_stemname and validate_path. These can be found at our GitHub repository as realpath-lib (full disclosure - this is our product but we offer it free to the community without any restrictions). It also could serve as a instructional tool - it's well documented.

We've tried our best to apply so-called 'modern Bash' practices, but Bash is a big subject and I'm certain there will always be room for improvement. It requires Bash 4+ but could be made to work with older versions if they are still around.

橘和柠 2024-08-02 05:07:21

我编写了 OS X 的 realpath 实用程序,它可以提供与 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

I wrote a realpath utility for OS X which can provide the same results as readlink -f.

Here is an example:

(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

If you are using MacPorts, you can install it with the following command: sudo port selfupdate && sudo port install realpath.

猫性小仙女 2024-08-02 05:07:21

真正独立于平台的也是这个 R-onliner

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

要真正模仿 readlink -f ,需要使用 $2 而不是 $1。

Truely platform-indpendent would be also this R-onliner

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

To actually mimic readlink -f <path>, $2 instead of $1 would need to be used.

蓝眸 2024-08-02 05:07:21

我只是将以下内容粘贴到 bash 脚本的顶部:

#!/usr/bin/env bash -e

declare script=$(basename "$0")
declare dirname=$(dirname "$0")
declare scriptDir
if [[ $(uname) == 'Linux' ]];then
    # use readlink -f
    scriptDir=$(readlink -f "$dirname")
else
    # can't use readlink -f, do a pwd -P in the script directory and then switch back
    if [[ "$dirname" = '.' ]];then
        # don't change directory, we are already inside
        scriptDir=$(pwd -P)
    else
        # switch to the directory and then switch back
        pwd=$(pwd)
        cd "$dirname"
        scriptDir=$(pwd -P)
        cd "$pwd"
    fi
fi

并删除了 readlink -f 的所有实例。 $scriptDir$script 然后将可用于脚本的其余部分。

虽然这并不遵循所有符号链接,但它适用于所有系统,并且对于大多数用例来说似乎足够好,它将目录切换到包含文件夹,然后执行 pwd -P 来获取该目录的真实路径,然后最后切换回原来的路径。

I have simply pasted the following to the top of my bash scripts:

#!/usr/bin/env bash -e

declare script=$(basename "$0")
declare dirname=$(dirname "$0")
declare scriptDir
if [[ $(uname) == 'Linux' ]];then
    # use readlink -f
    scriptDir=$(readlink -f "$dirname")
else
    # can't use readlink -f, do a pwd -P in the script directory and then switch back
    if [[ "$dirname" = '.' ]];then
        # don't change directory, we are already inside
        scriptDir=$(pwd -P)
    else
        # switch to the directory and then switch back
        pwd=$(pwd)
        cd "$dirname"
        scriptDir=$(pwd -P)
        cd "$pwd"
    fi
fi

And removed all instances of readlink -f. $scriptDir and $script then will be available for the rest of the script.

While this does not follow all symlinks, it works on all systems and appears to be good enough for most use cases, it switches the directory into the containing folder, and then it does a pwd -P to get the real path of that directory, and then finally switch back to the original.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文