给定绝对或相对路径(在类Unix系统中),我想在解析任何中间符号链接后确定目标的完整路径 . 奖励积分也可以同时解析〜用户名表示法 .
如果目标是目录,则可以将chdir()放入目录然后调用getcwd(),但我真的想从shell脚本而不是编写C帮助程序 . 不幸的是,shell倾向于试图隐藏用户的符号链接(这是OS X上的bash):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
我想要的是一个函数resolve(),这样当从上面例子中的tmp目录执行时,解析(“foo”)==“/ Users / greg / tmp / bar” .
16 回答
根据标准,
pwd -P
应该返回解析符号链接的路径 .来自
unistd.h
的C函数char *getcwd(char *buf, size_t size)
应该具有相同的行为 .getcwd pwd
编者注:以上内容适用于GNU readlink和FreeBSD / PC-BSD / OpenBSD readlink,但从10.11开始不适用于OS X. GNU readlink提供了其他相关选项,例如-m用于解析符号链接,无论最终目标是否存在 .
请注意,自GNU coreutils 8.15(2012-01-06)以来,有一个 realpath 程序可用,比上面的程序更简洁,更灵活 . 它还与同名的FreeBSD util兼容 . 它还包括生成两个文件之间的相对路径的功能 .
[管理员在下面添加评论halloleo - [danorton]](/users/65889/danorton)
对于Mac OS X(至少10.11.x),请使用不带
-f
选项的readlink
:编者注:这不会递归地解析符号链接,因此不会报告最终目标;例如,给定符号链接a指向b,b又指向c,这将仅报告b(并且不会确保它作为绝对路径输出) . 在OS X上使用以下perl命令来填补缺少的readlink -f功能的空白:perl -MCwd -le'print Cwd :: abs_path(shift)'“$ path”
如果您只是想要目录,“pwd -P”似乎有效,但如果由于某种原因您想要实际可执行文件的名称,我认为这没有帮助 . 这是我的解决方案:
我最喜欢的一个是
realpath foo
似乎正是你所要求的 - 它接受一个arbirary路径,解析所有符号链接,并返回“真实”路径 - 它可能是所有系统已经拥有的“标准* nix”
其他方式:
将一些给定的解决方案放在一起,知道readlink在大多数系统上都可用,但需要不同的参数,这对我来说在OSX和Debian上运行良好 . 我不确定BSD系统 . 也许条件需要
[[ $OSTYPE != darwin* ]]
才能从OSX中排除-f
.注意:我相信这是一个坚固,便携,现成的解决方案,因此非常冗长 .
下面是 fully POSIX-compliant script / function ,因此 cross-platform (也适用于macOS,其
readlink
仍然不支持-f
,截至10.12(Sierra)) - 它仅使用POSIX shell language features且仅使用POSIX兼容的实用程序调用 .它是 portable implementation of GNU's readlink -e (
readlink -f
的更严格版本) .你可以 run the script with sh 或 source the function in bash, ksh, and zsh :
例如,在脚本内部,您可以按如下方式使用它来获取正在运行的脚本真正的原始目录,并解决了符号链接:
rreadlink script / function definition:
代码改编了这个答案的感激之情 . 我还在这里创建了一个基于bash的独立实用程序版本,如果安装了Node.js,可以使用npm install rreadlink -g进行安装 .
A tangent on security:
jarno,参考确保内置
command
没有被同名的别名或shell函数遮蔽的函数,请在注释中询问:rreadlink
确保command
具有其原始含义的动机是使用它来绕过(良性)方便别名和通常用于影响交互式shell中的标准命令的函数,例如重新定义ls
以包括收藏选项 .我认为's safe to say that unless you'正在处理一个不受信任的恶意环境,担心
unalias
或unset
- 或者,就此而言,while
,do
,...... - 被重新定义并不是一个问题 .功能必须依赖于具有其原始含义和行为的东西 - 没有办法解决这个问题 .
类似POSIX的shell允许重新定义内置函数甚至语言关键字本身就存在安全风险(编写偏执代码通常很难) .
为了解决您的疑虑:
该函数依赖
unalias
和unset
具有其原始含义 . 让它们以改变其行为的方式重新定义为shell函数将是一个问题;重新定义作为别名不一定是一个问题,因为引用(部分)命令名称(例如,\unalias
)绕过别名 .但是,引用不是shell关键字的选项(
while
,for
,if
,do
,...),而shell关键字确实优先于shell函数,在bash
和zsh
别名中具有最高优先级,因此要防止shell-关键字重定义必须使用它们的名称运行unalias
(尽管在非交互式bash
shell(例如脚本)中,默认情况下不会扩展别名 - 仅当首先显式调用shopt -s expand_aliases
时) .要确保
unalias
- 作为内置函数 - 具有其原始含义,您必须首先使用\unset
,这要求unset
具有其原始含义:unset
是一个内置的shell,所以为了确保它被调用,你必须确保它本身没有被重新定义为一个函数 . 虽然您可以使用引号绕过别名表单,但您无法绕过shell函数表单 - catch 22 .因此,除非你能依靠
unset
具有其原始含义,从我所知道的,没有保证的方法来抵御所有恶意重新定义 .常见的shell脚本通常必须找到它们的“home”目录,即使它们被作为符号链接调用 . 因此,剧本必须从0美元找到他们的“真实”位置 .
在我的系统上打印一个包含以下内容的脚本,这应该是您需要的一个很好的提示 .
试试这个:
因为我多年来遇到过这么多次,而这次我需要一个可以在OSX和linux上使用的纯bash便携版本,我继续编写一个:
活着的版本住在这里:
https://github.com/keen99/shell-functions/tree/master/resolve_path
但是为了SO,这是现在的版本(我觉得它经过了很好的测试......但我很乐意接受反馈!)
可能并不难让它适用于普通的bourne shell(sh),但我没试过......我太喜欢$ FUNCNAME了 . :)
这是一个经典的例子,感谢brew:
使用此函数,它将返回-real-路径:
为了解决Mac不兼容问题,我想出了办法
不太好,但跨OS
您的路径是目录,还是文件?如果它是一个目录,那很简单:
但是,如果它可能是一个文件,那么这将不起作用:
因为符号链接可能会解析为相对路径或完整路径 .
在脚本上我需要找到真正的路径,以便我可以引用与它一起安装的配置或其他脚本,我使用这个:
您可以将
SOURCE
设置为任何文件路径 . 基本上,只要路径是符号链接,它就会解析该符号链接 . 诀窍在循环的最后一行 . 如果已解析的符号链接是绝对的,则将其用作SOURCE
. 但是,如果它是相对的,它将为它添加DIR
,它通过我首先描述的简单技巧解析为真实位置 .我相信这是使用Bash解决符号链接的真正和明确的“方法”,无论是目录还是非目录:
请注意,所有
cd
和set
内容都在子shell中进行 .以下是使用内联Perl脚本在MacOS / Unix中获取文件实际路径的方法:
同样,要获取符号链接文件的目录: