如何从OS X检索任意文件的绝对路径


18

我正在寻找一个可以在Bash中使用的简单命令,以找到OS X上文件的绝对和规范化路径(类似于Linux下的``readlink -f''`)。

以下示例bash会话描述了一种名为``abspath''的[虚拟]实用程序,该实用程序具有所需的行为:

$ pwd
/Users/guyfleegman

$ ls -lR
drwxr-xr-x  4 guyfleegman  crew  136 Oct 30 02:09 foo

./foo:
-rw-r--r--  1 guyfleegman  crew  0 Oct 30 02:07 bar.txt
lrwxr-xr-x  1 guyfleegman  crew  7 Oct 30 02:09 baz.txt -> bar.txt


$ abspath .
/Users/guyfleegman

$ abspath foo
/Users/guyfleegman/foo

$ abspath ./foo/bar.txt
/Users/guyfleegman/foo/bar.txt

$ abspath foo/baz.txt
/Users/guyfleegman/foo/baz.txt

与上例中最后一次调用``abspath''一样,我希望它不会自动解析符号链接,但是在这里我不会太挑剔。


Answers:


16
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }

例子:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store

11

我认为没有内置命令可以执行此操作。杰西·威尔逊为此写了一个bash脚本:

#!/bin/bash
cd -P -- "$(dirname -- "$1")" &&
printf '%s\n' "$(pwd -P)/$(basename -- "$1")"

但是,它不适用于直接在下面的路径/,例如/etc(printing //etc)以及.and ../cwd/.两种情况下均打印)。我尝试对其进行修改,但是我的bash-fu不足,使我失败了。

这是我的建议:

#!/usr/bin/env python
import os.path
import sys

for arg in sys.argv[1:]:
    print os.path.abspath(arg)

另存为/usr/bin/abspath或类似的东西并使其可执行。样本输出:

Servus08:~ danielbeck$ abspath .
/Users/danielbeck
Servus08:~ danielbeck$ abspath /tmp
/tmp
Servus08:~ danielbeck$ abspath Documents
/Users/danielbeck/Documents
Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/tmp
/Users/danielbeck/Documents

如果您确实想要符号链接解析,请print像这样更改行:

    print os.path.realpath(os.path.abspath(arg))

得到这个:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents

1
我认为您使用第一种基于shell的方法是正确的,但是我相信使用另一种语言来实现此目的会有点目的,因为它引入了附加的依赖关系,并且几乎等同于仅编译GNU版本的`readlink (1)”(在OS X下)(假设可以完成;我尚未验证)。
Michael Wehner

2
@Michael您是在具有GNU readlink的系统上,还是在OS X上-对吗?OS X开箱即可使用Python。从理论上讲,这是一个新的依赖,实际上不是。无论如何,这就是我所能提供的。
丹尼尔·贝克

您所暗示的务实方法过于简单,值得称赞。无论如何,如果我们要采用这种方法来代替纯粹用bash编写的任何内容(这是绝对可行的,并且不依赖于其他任何东西-很难在一行中完成),那么Python可能不是不是最好的选择。Perl和Ruby(可能还有PHP)都可以在命令行上简洁地执行此操作,而无需创建实际的脚本文件。
Michael Wehner 2010年

1
@Michael:是的,但这不是我的评论。我为您提供了由Jesse Wilson用纯bash编写的90%解决方案,以及为何只有90%的分析。如果那对您没问题,那就很好。我还为您提供了一个稍微复杂一点的Python 100%解决方案。尽管其他脚本语言可能更简短(Perl臭名昭著,所以:-)),但是所有语言都需要一个附加文件来存储命令。还是您想在每次使用时将其写出来?这也是为什么我添加了多行功能来处理多个参数的原因,即1或2行然后保持不变。
丹尼尔·贝克

2
@Michael,为什么Python在OS X上不是一个好选择?它甚至附带实际上是Python脚本的命令,例如file /usr/bin/xattr
Arjan 2010年

8

一种选择是仅安装coreutils并使用greadlink -f。它解析符号链接,并且可以与/Foo/or 链接(~/foo.txt如果不存在)一起使用,但是与/Foo/foo.txtif /Foo/不存在一起使用。

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

这不能解析符号链接,也不能/Foo/foo.txt使用。

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath ~/Foo/foo.txt # doesn't work
abspath ~/Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store
abspath ~/Documents/Books/

dirs -l执行波浪线扩展。dirs +0如果堆栈中还有其他目录,则仅输出最顶层的目录。


2

我猜你可以用python或ruby做到这一点。

$ ruby -e 'puts File.expand_path("~/somepath")'

或使用以下命令

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])

我之前对Daniel Beck的答案的评论也适用于此(我更喜欢纯Bash-y解决方案),尽管如果我要使用另一种语言来实现此目的,那么到目前为止我还是最喜欢您的解决方案。:)我可能还会包装对ruby的调用(例如,函数abspath(){ruby -e“ puts File.expand_path('$ 1')”;}),并将其放入我的`.profile'中。
Michael Wehner

1

如果您为perl安装了File :: Spec模块,则可以执行以下操作:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'

0

对于bash / sh脚本,可以使用此递归函数:

canonical_readlink ()
{
    cd `dirname $1`;
    __filename=`basename $1`;
    if [ -h "$__filename" ]; then
        canonical_readlink `readlink $__filename`;
    else
        echo "`pwd -P`";
    fi
}

answer=$(canonical_readlink $0)

某些变量和命令替换未正确引用。您可以使用局部变量而不是类似的变量名__filename。该脚本当前的行为更像是dirname
Lri 2012年

0

为OSX安装以下库:

brew install coreutils

greadlink -f file.txt

0

如果无法安装coreutils,则以下内容可处理符号链接的组合。和..并能像GNU realpath一样处理文件和文件夹:

#!/usr/bin/env bash
realpath()
{
    if ! pushd $1 &> /dev/null; then 
        pushd ${1##*/} &> /dev/null
        echo $( pwd -P )/${1%/*}
    else
        pwd -P
    fi
    popd > /dev/null
}

但是它不支持realpath的--relative-to。这将需要/programming//a/12498485/869951


0

鉴于约束是MacOS(当时为OS X),默认情况下可用PHP。这将返回文件的根目录。也删除dirname以获取文件。

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.