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

Why does boost::filesystem::canonical() require the target path to exist?

本文关键字:目标路径 存在 boost filesystem canonical 为什么      更新时间:2023-10-16

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

Overview:将必须存在的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与符号链接解析和结果标准化。返回:一个路径,它是在p的前导元素(如果存在)和p的不存在元素(如果存在)组成的路径上调用canonical()函数的结果。"

编辑3:

关于std::filesystem的更多讨论

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

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

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

OP的评论下面质疑我的说法,即filesystem::canonicalrealpath实现相同的操作,但在N4100和POSIX的定义对我来说似乎几乎相同,比较:

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

:

将必须存在的p转换为不包含符号链接、"."".."元素的绝对路径。

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

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

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

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

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

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

即使有,难道没有理由创建一个没有这个限制的函数的变体吗?(如果没有这样的变体,获取路径需要手动复制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);
}