如何找到两条绝对路径的相对路径?

给出两条绝对路径,例如

  • /a/path/to/a
  • /a/path/to/somewhere/else

我怎样才能获得从一个到另一个的相对路径, ../a

从某种意义上说,与realpath相反。

我在这里回答了类似的问题: 解析相对路径而不引用Windows上的当前目录 。

这没有标准function。 为此,vi-like-emacs中有一个函数。 快速检查apropos relative显示我很少有其他程序可能实现这个:例如revpath 。

它可以作为字符串操作(不需要计算工作目录)来完成:

  • 首先找到以路径分隔符结尾的最长公共前缀。
  • 如果没有共同的前缀,那么你就完成了
  • 从当前和目标字符串的(…的副本)中删除公共前缀
  • 用“..”替换当前字符串中的每个目录名
  • 在目标字符串前面添加(使用路径分隔符)
  • 返回组合字符串

第二步中的“ 完成 ”假定您希望使用相对路径来缩短结果。 另一方面,无论长度如何,您可能都希望使用相对路径名。 在这种情况下,只需跳过该步骤(结果将更长,但相对 )。

找到最长的公共路径(在本例中为/a/path/to )并从两个绝对路径中删除它。 这会给:

  • /a
  • /somewhere/else

现在,用../替换起始路径中的每个路径组件,并将结果前置到目标路径。 如果你想从目录else转到目录a ,那会给你:

 ../../a 

如果你想走另一条路,你会改为:

 ../somewhere/else 

使用第一个绝对路径构建树,然后将第二个路径添加到该树,然后从一个叶子走到另一个叶子:从一个节点到其父节点的步骤被转换为“../”序列和一个步骤从一个节点到其子节点之一被转换为该子节点的名称。 请注意,可能有多个解决方案。 例如:

1) /a/path/to/a

2) /a/path/to/a/new/one

从(1)到(2)的明显路径是new/one但是../../../a/path/to/a/new/one也是有效的。 当您编写算法来在树中行走时,您必须意识到这一点

这是在“ln”命令中实现的,该命令是GNU Coreutils包的一部分(用于ln -r )。 显然,有很多方法可以解决这个问题,甚至可以从自己提出解决方案中获得一些好处,而无需查看现有代码。 就个人而言,我发现Coreutils中的代码非常有启发性。

如果我必须将绝对路径转换为C项目中的相对路径,我只需从Coreutils复制“relpath.c”。 它与包中的其他实用程序函数有一些依赖关系,这些也需要以某种forms引入。 这是主要的“relpath()”函数。 请注意,它适用于规范化的路径名,例如,它不喜欢路径包含“//”或“/。”之类的东西。 基本上它找到两个路径的公共前缀,然后它根据每个路径的剩余部分是否为空来处理出现的四种情况。

 /* Output the relative representation if possible. If BUF is non-NULL, write to that buffer rather than to stdout. */ bool relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len) { bool buf_err = false; /* Skip the prefix common to --relative-to and path. */ int common_index = path_common_prefix (can_reldir, can_fname); if (!common_index) return false; const char *relto_suffix = can_reldir + common_index; const char *fname_suffix = can_fname + common_index; /* Skip over extraneous '/'. */ if (*relto_suffix == '/') relto_suffix++; if (*fname_suffix == '/') fname_suffix++; /* Replace remaining components of --relative-to with '..', to get to a common directory. Then output the remainder of fname. */ if (*relto_suffix) { buf_err |= buffer_or_output ("..", &buf, &len); for (; *relto_suffix; ++relto_suffix) { if (*relto_suffix == '/') buf_err |= buffer_or_output ("/..", &buf, &len); } if (*fname_suffix) { buf_err |= buffer_or_output ("/", &buf, &len); buf_err |= buffer_or_output (fname_suffix, &buf, &len); } } else { buf_err |= buffer_or_output (*fname_suffix ? fname_suffix : ".", &buf, &len); } if (buf_err) error (0, ENAMETOOLONG, "%s", _("generating relative path")); return !buf_err; } 

您可以在此处查看“relpath.c”的其余部分。 “ln.c”中有一个更高级别的包装函数,它在调用relpath之前规范化其路径名参数,它被命名为convert_abs_rel() 。 这可能是你想要在大多数时间打电话的。