Windows和Linux之间的相对路径分辨率差异?

Posted

技术标签:

【中文标题】Windows和Linux之间的相对路径分辨率差异?【英文标题】:Relative path resolution differences between Windows & Linux? 【发布时间】:2020-06-11 15:20:37 【问题描述】:

给定 CWD 中的文件text.txt,我从fopen() 得到两个不同的答案,即“此文件是否存在?”这个问题。对于a/b/../../test.txt(减少为test.txt):

Windows:文件存在 Linux:文件(!)存在

但是,如果我随后 mkdir -p a/b Linux 改变其调子并说 a/b/../../test.txt 现在存在。

就好像 Linux 在处理相对路径时检查每个目录是否存在(如果其中任何一个不存在,则提前输出),而不是先折叠任何 ../,然后像 Windows 一样进行文件存在检查似乎可以。

两个问题:

在这种情况下,如何让 Linux 像 Windows 一样运行?像某种环境变量来修改 glibc/syscall 行为。 应该fopen() 如何处理这样的相对路径?我可以为fopen() 找到的文档/规范只是表明它接受了相对路径(也许有几个例子),而不是在中间路径../ 的情况下应该如何解决这些路径。

测试程序:

#include <filesystem>
#include <fstream>
#include <iostream>
#include <cstdio>

void file_exists( const char* filename )

    FILE* file = fopen( filename, "r" );
    std::cout << ( file ? "Y" : "N" ) << ": " << filename << std::endl;
    if( file )
    
        fclose( file );
    


int main( int, char** )

    // initial tests
    std::filesystem::remove( "test.txt" );
    std::filesystem::remove( "doesnt-exist.txt" );
    file_exists( "test.txt" );
    file_exists( "doesnt-exist.txt" );
    std::cout << std::endl;

    // create file
    std::cout << "creating text.txt..." << std::endl;
    std::ofstream output( "test.txt", std::ios::trunc );
    output << "test" << std::endl;
    output.close();

    // more tests
    file_exists( "test.txt" );
    file_exists( "doesnt-exist.txt" );
    file_exists( "a/b/../../test.txt" );
    file_exists( "a/b/../../doesnt-exist.txt" );
    std::cout << std::endl;

    std::cout << "creating directory 'a/b'..." << std::endl;
    std::filesystem::create_directories( "a/b" );
    file_exists( "a/b/../../test.txt" );
    file_exists( "a/b/../../doesnt-exist.txt" );
    std::cout << std::endl;

    // cleanup
    std::filesystem::remove( "test.txt" );
    std::filesystem::remove_all( "a/b" );

    return 0;

结果:

Windows 10、VS2017:

N: test.txt
N: doesnt-exist.txt

creating text.txt...
Y: test.txt
N: doesnt-exist.txt
Y: a/b/../../test.txt          # file exists, expected behavior
N: a/b/../../doesnt-exist.txt

creating directory 'a/b'...
Y: a/b/../../test.txt
N: a/b/../../doesnt-exist.txt

Ubuntu 20.04,g++ (Ubuntu 9.3.0-10ubuntu2) 9.3.0

N: test.txt
N: doesnt-exist.txt

creating text.txt...
Y: test.txt
N: doesnt-exist.txt
N: a/b/../../test.txt          # file *doesn't* exist, unexpected behavior
N: a/b/../../doesnt-exist.txt

creating directory 'a/b'...
Y: a/b/../../test.txt
N: a/b/../../doesnt-exist.txt 

【问题讨论】:

在这种情况下,Windows 行为可能对您更方便,但我认为 Ubuntu 行为看起来更“正确”。如果以这种方式指定路径,也许它应该与指定 c/d/../../text.txt 不同。如果“a/b”或“c/d”(以指定者为准)不存在,则所写的路径是没有意义的。 Windows底层NT系统不支持“.”和路径中的“..”组件,而不是在某些文件系统中保留名称(例如 NTFS 保留它们;FAT32 不允许并允许创建名为“.”和“..”的文件)。因此,当将路径规范化为 NT 路径时,Windows API 必须将它们解析为纯字符串操作(例如,在调用 NtCreateFile 之前规范化 CreateFileW 中的路径)。 @Basya:您当然希望操作系统会检查a 中的b 的权限。 @dxiv:The cppreference page for std::filesystem::path 表示路径规范化是在 Windows 样式中完成的,没有文件系统检查。 一个例外是 NT 中的文件系统相关符号链接,它在内核中相对于符号链接的路径进行重新解析,其语义类似于 Unix——当且仅当组件存在时(即非- 存在“a”和“b”)。这是正确的方法,因为天真地解析“..”以删除符号链接(例如“symlink/../spam”)可能会导致完全不同的目标路径。因此,“..”组件的处理在 Windows 中是自相矛盾的,与 Unix 相比,这使得手动解析 Windows 中的相关符号链接(例如通过 readlink 实现)极其困难。 【参考方案1】:

文件名和路径名是特定于实现的。当您意识到 C:X 在 Linux 和 Windows 中是一个完全有效的文件名时,这显然是有道理的,但含义却截然不同。

在 Linux(如 UNIX)上,.. 是一个真正的目录条目。也就是说a/b/../../涉及4个真实的目录项:a和b明显,但也有b中的..和a中的..fopen 不需要做任何特别的事情:操作系统只检查实际的目录条目。

在 Windows 上,必须首先将路径 a/b/../../ 转换为标准格式。也就是说应该以\??\开头。您以前可能没有见过这些路径,但它们是 Windows 内部使用的。所有这些路径都是绝对路径,并且这些 always 包含反斜杠。因此,从a/b/../../ 的翻译涉及为当前工作驱动器和目录添加前缀,将\ 替换为/ 并删除..

如您所见,这些是解决.. 的完全不同的方法。您的 fopen 调用仅遵循本地约定,通常只需将字符串传递给操作系统。 (但在 Windows 上,fopen 也可能提前进行 /\ 转换,以确保。)

如果另一个操作系统选择^ 来表示“父目录”,那么您会期望fopen("a/b/^/^") 也遵循该约定,所以它不像.. 是一个特殊的字符串。在这样的平台上,.. 甚至可能是子目录的有效名称。

【讨论】:

请注意,如果您在 Windows 中使用逐字“\\?\”路径,您实际上可以在 FAT32 文件系统中创建一个名为“..”的文件。它与非根目录列表中的普通“..”条目的区别在于自动生成的 8.3 短名称。【参考方案2】:

std::filesystem::path::lexically_normal 可能有助于纯词法规范化路径(不遵循符号链接,检查路径是否存在,...)。

所以fopen(std::filesystem::path"a/b/../../test.txt".lexically_normal().c_str(), "r") 的行为相同。 (你确实必须包装你的电话才能有这种行为)。

【讨论】:

以上是关于Windows和Linux之间的相对路径分辨率差异?的主要内容,如果未能解决你的问题,请参考以下文章

Windows和Linux之间的文件差异[关闭]

使用 Intel 编译器的 Windows 和 Linux 之间的性能差异:查看程序集

Linux获取两个路径之间的相对路径

为啥 Windows、Linux 等汇编语言之间存在差异?

java File linux windows 下 绝对路径 相对路径问题

Linux上使用程序相对路径访问文件