<p><code>relpath</code>有意外行为。它将路径的所有元素视为一个目录。所以,在这条路上:</p>
<p><code>/path/to/a/file.txt</code><code>file.txt</code>也被视为一个目录。</p>
<p>这意味着当您在两条路径上运行<code>relpath</code>时</p>
<pre class="lang-py prettyprint-override"><code>>>> from os.path import relpath
>>> relpath('/path/to/dest/file.txt', '/path/to/origin/file.txt')
'../../dest/file.txt'
</code></pre>
<p>这是不正确的。从目录源到目标的真正相对路径是<code>'../dest/file.txt'</code></p>
<p>如果你试图创建符号链接,但最终它们的格式不正确,这会特别令人沮丧。</p>
<h2>解决方案</h2>
<p>要解决这个问题,我们必须首先找出路径是否指向一个文件,如果不是,我们可以像往常一样进行比较,否则我们需要从末尾删除文件名,只与目录进行比较,然后将文件添加回末尾。</p>
<p>注意,只有在系统上实际创建了这些文件时,这才有效,python必须访问文件系统才能找到节点类型。</p>
<pre class="lang-py prettyprint-override"><code>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)
</code></pre>
<p>要获取从目录源到目标的实际相对路径,请运行:</p>
<pre class="lang-py prettyprint-override"><code>>>> relrealpath('/path/to/origin/file.txt', '/path/to/dest/file.txt')
'../dest/file.txt'
</code></pre>
<p>我颠倒了参数顺序,因为在我的大脑中,更合理的说法是,“我想知道从arg1到arg2的相对路径”,标准的<code>relpath</code>实现是向后的(可能是因为UNIX就是这样做的)。</p>
<p>这种访问文件系统的需要是<code>relpath</code>具有如此奇怪行为的真正原因。文件系统调用很昂贵,所以python让您知道您是在处理文件还是在处理目录,并且只在您提供的路径上执行字符串操作。</p>
<p>注意:可能有一种方法可以使<code>realrelpath</code>函数更有效率。例如,我不确定是否需要<code>abspath</code>调用,或者是否可以通过返回更多信息的syscall与<code>os.path.isfile</code>检查绑定。我欢迎改进。</p>