relpath
有意外的行为。它将路径的所有元素视为目录。所以,在路径中:
/path/to/a/file.txt
file.txt
也被视为目录。
这意味着当你relpath
在两条路径上运行时,比如说,
>>> from os.path import relpath
>>> relpath('/path/to/dest/file.txt', '/path/to/origin/file.txt')
'../../dest/file.txt'
这是不正确的。从目录起点到终点的真正相对路径是'../dest/file.txt'
如果您尝试创建符号链接并且它们最终格式错误,这尤其令人沮丧。
解决方案
要解决这个问题,我们首先要找出路径是否指向一个文件,如果不是我们可以照常进行比较,否则我们需要从最后删除文件名,仅与目录进行比较,然后添加文件回到最后。
请注意,这仅在您实际上在系统上创建了这些文件时才有效,python 必须访问文件系统以查找节点类型。
import os
def realrelpath(origin, dest):
'''Get the relative path between two paths, accounting for filepaths'''
# get the absolute paths so that strings can be compared
origin = os.path.abspath(origin)
dest = os.path.abspath(dest)
# find out if the origin and destination are filepaths
origin_isfile = os.path.isfile(origin)
dest_isfile = os.path.isfile(dest)
# if dealing with filepaths,
if origin_isfile or dest_isfile:
# get the base filename
filename = os.path.basename(origin) if origin_isfile else os.path.basename(dest)
# in cases where we're dealing with a file, use only the directory name
origin = os.path.dirname(origin) if origin_isfile else origin
dest = os.path.dirname(dest) if dest_isfile else dest
# get the relative path between directories, then re-add the filename
return os.path.join(os.path.relpath(dest, origin), filename)
else:
# if not dealing with any filepaths, just run relpath as usual
return os.path.relpath(dest, origin)
要获取从目录源到目标的真实相对路径,请运行:
>>> relrealpath('/path/to/origin/file.txt', '/path/to/dest/file.txt')
'../dest/file.txt'
我颠倒了参数顺序,因为在我的大脑中说“我想知道从 arg1 到 arg2 的相对路径”更有意义,标准relpath
实现将其倒退(可能是因为 UNIX 就是这样做的)。
这种访问文件系统的需要是relpath
产生这种奇怪行为的真正原因。文件系统调用很昂贵,因此 python 让您知道您是在处理文件还是目录,并且只在您提供的路径上执行字符串操作。
注意:可能有一种方法可以使realrelpath
函数更高效。例如,我不确定这些abspath
调用是否必要,或者它们是否可以与os.path.isfile
返回更多信息的系统调用的检查捆绑在一起。我欢迎改进。