17

状态的文档boost::filesystem::canonical(const path& p)

概述:将必须存在的 p 转换为没有符号链接、点或点-点元素的绝对路径。
...
备注: !exists(p) 是一个错误。

这样做的结果是,如果 p 标识其目标不存在的符号链接,则该函数将失败file not found并且不返回路径。

这对我来说似乎过于严格:仅仅因为链接的目标不存在,我看不出函数无法解析该不存在目标的路径的原因。(相比之下,absolute()没有这样的限制。)

(显然,如果路径中的符号链接损坏,则无法解析目标路径。)

那么,这种限制有正当理由吗?

即使有,是否也有理由创建一个没有此限制的函数的变体?canonical()(如果没有这样的变体,获取路径需要手动复制 99% 的已经完成的操作,这很容易出错。)

我很欣赏这种情况之间存在stat()lstat()同样适用于这种情况的语义微妙之处——这正是我认为函数的变体同样合理的原因。

注意:这个问题同样std::experimental::filesystem适用基于boost::filesystem.

编辑:

在@Jonathan Wakeley 下面的知识渊博的回答之后,我仍然保留了我最初问题的本质,我将稍微重新构建:

  • 是否存在要求目标存在的潜在技术或逻辑原因boost::filesystem::canonical()我的意思是,目标的不存在是否以某种方式使解决规范形式的路径变得不可能?

  • 如果没有,是否有任何技术或逻辑理由提出仅与现有形式不同的功能变体,因为它不需要目标存在?

  • 在转换为boost::filesystem提议的 N4100的过程中(据我了解是这种情况) std::experimental::filesystem,是否在经过适当考虑后采用了这种限制canonical(),还是只是从 Boost 定义中“落空”?

编辑2:

我注意到 Boost 1.60 现在提供了以下功能weakly_canonical():“返回 p 并解析符号链接并规范化结果。返回:在canonical()由 p 的前导元素组成的路径上调用函数的结果组成的路径,如果存在的话,跟随p 中不存在的元素,如果有的话。”

编辑 3:

更多关于这个的讨论std::filesystem

4

2 回答 2

10

尝试weakly_canonical()它不需要在mac上存在路径

于 2017-04-07T16:11:22.263 回答
6

基本上是因为它是具有相同要求的realpath的包装器。

你可以问同样的问题realpath,但我认为答案是,如果你试图找出路径名所指的真实的物理文件或目录,那么如果它是一个损坏的符号链接,那么没有答案,它引用真实的文件或目录,所以你想要一个错误。

下面的 OP 评论质疑我的说法filesystem::canonicalrealpath实施相同的操作,但 N4100 和 POSIX 中的定义似乎与我几乎相同,比较:

realpath()函数应从 指向的路径名派生file_name一个绝对路径名,该路径名解析为相同的目录条目,其解析不涉及'.''..'或符号链接。

和:

p必须存在的 转换为没有符号链接"."、、或".."元素的绝对路径。

在这两种情况下,要求是:

  • no symbolic links,如果它返回最后一个组件是符号链接的路径,则不会满足该要求。

  • 规范路径指的是存在的东西,这在 N4100 中是明确的,在 POSIX 中是隐含的,因为它指向某个目录条目(即存在的东西)并且目录条目不是符号链接(因为第一个要求)。

至于为什么这些应该是要求,N4100 中的注释很有帮助:

[注意:规范路径名允许对路径进行安全检查(例如,此路径是否存在/home/goodguy/home/badguy?)-结束注释]

正如我上面已经说过的,如果即使路径是实际上指向任何东西的符号链接,它也成功返回,那么您需要做额外的工作来检查它是否解析为真实文件,从而实现预期的用例不太方便。

即使有,是否也有理由创建一个没有此限制的函数的变体?(如果没有这样的变体,获取路径需要手动复制 canonical() 已经完成的 99% 的容易出错的操作。)

可以说,该变体不太常用,因此不应该是默认值,但是如果您需要它,则不难做到:

// like canonical() but allows the last component of p to be a broken symlink
filesystem::path
resolve_most_symlinks(filesystem::path const& p, filesystem::path const& base = filesystem::current_path())
{
  if (is_symlink(p) && !exists(p))
    return canonical(absolute(p, base).remove_filename()) / p.filename();
  return canonical(p);
}
于 2015-07-10T10:20:46.443 回答