为什么boost :: filesystem :: canonical()要求目标路径存在?

Jer*_*emy 13 c++ boost boost-filesystem c++17

boost::filesystem::canonical(const path& p)各州的文件:

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

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

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

(显然,如果一个符号链接的路径被打破,目标路径不能被解决.)

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

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

我理解在这种情况之间存在的语义细微之处stat()并且lstat()同样适用于这种情况 - 这正是我认为函数变体同样合理的原因.

注意:这个问题同样适用于基于的std::experimental::filesystem库(n4100)boost::filesystem.

编辑:

在@Jonathan Wakeley下面非常知识渊博的答案之后,我仍然留下了原始问题的精髓,我将稍微重新思考:

  • 是否有一个基本的技术或逻辑上的理由,为什么boost::filesystem::canonical()要求目标存在?我的意思是,目标的不存在是否会使得无法解决规范形式的道路?

  • 如果没有,是否有任何技术或逻辑上的理由提出,只有从现有的形式,不同之处在于它的功能的变化要求目标存在?

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

编辑2:

我注意到Boost 1.60现在提供了这样的功能weakly_canonical():"返回p,symlinks已解析,结果归一化.返回:一个路径,由在canonical()p的前导元素组成的路径上调用函数的结果组成,如果有,则按照由p的元素,如果有的话,不存在."

编辑3:

这更多的讨论有关std::filesystem.

小智 8

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


Jon*_*ely 6

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

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

下面的OP评论质疑我的说法filesystem::canonicalrealpath执行相同的操作,但N4100和POSIX中的定义看起来几乎与我相同,比较:

realpath()功能将派生,从路径指向file_name,绝对路径解析到同一个目录项,其分辨率不涉及'.','..'或符号链接.

和:

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

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

  • 没有符号链接,如果它返回了一个路径,其中最后一个组件是符号链接,不符合要求.

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

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

[ 注:规范路径名允许路径的安全检查(如没有此路径住在/home/goodguy/home/badguy?)末端注 ]

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

即使存在,是否也没有理由创建没有此限制的函数变体?(没有这样的变体,获得路径需要容易出错的手动复制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);
}
Run Code Online (Sandbox Code Playgroud)