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

问题描述 投票:1回答:2

给定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 
    
c++ linux windows fopen relative-path
2个回答
1
投票

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

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

在Windows上,必须首先将路径a/b/../../转换为标准格式。也就是说,它应该以\??\开头。您可能以前没有看过这些路径,但是它们是Windows内部使用的路径。所有这些路径都是绝对路径,并且这些always

包含反斜杠。因此,从a/b/../../的转换涉及为当前工作的驱动器和目录添加前缀,用\替换/并删除..

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

[如果另一个OS选择^表示“父目录”,则您希望fopen("a/b/^/^")也遵循该约定,因此..不是一个特殊的字符串。在这样的平台上,..甚至可能是子目录的有效名称。


0
投票

[std::filesystem::path::lexically_normal可能有助于纯粹按词法标准化路径(不遵循simlink,检查路径是否存在,...)。

© www.soinside.com 2019 - 2024. All rights reserved.