为何std::filesystem::is_regular_file对符号链接返回true?
我发现std::filesystem中的is_regular_file函数会对符号链接返回true,查阅相关文档未找到原因,且cppreference上的示例也存在相同行为。
以二进制文件pidof为例:/usr/sbin/pidof是符号链接,stat命令输出如下:
[@rhel9 ~]$ stat /usr/sbin/pidof File: /usr/sbin/pidof -> /usr/bin/pidof Size: 14 Blocks: 0 IO Block: 4096 symbolic link Device: fd00h/64768d Inode: 34952482 Links: 1 Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:bin_t:s0 Access: 2024-02-05 15:46:24.124158991 +0000 Modify: 2023-01-28 09:40:21.000000000 +0000 Change: 2023-05-09 17:34:59.432002380 +0100 Birth: 2023-05-09 17:34:59.432002380 +0100
追踪该链接后执行stat命令,/usr/bin/pidof是普通文件:
[@rhel9 ~]$ stat /usr/bin/pidof File: /usr/bin/pidof Size: 23760 Blocks: 48 IO Block: 4096 regular file Device: fd00h/64768d Inode: 18153014 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:bin_t:s0 Access: 2024-02-02 15:12:41.804334284 +0000 Modify: 2023-01-28 09:40:21.000000000 +0000 Change: 2023-05-09 17:34:59.418002380 +0100 Birth: 2023-05-09 17:34:59.417002380 +0100
运行以下测试代码:
const std::filesystem::path fsBin("/usr/sbin/pidof"); if (true == std::filesystem::is_regular_file(fsBin)) { std::cout << "Regular File" << std::endl; } else if(true == std::filesystem::is_symlink(fsBin)) { std::cout << "Symbolic Link" << std::endl; }
代码始终输出"Regular File",尽管该路径是符号链接。若调换判断顺序(先判断is_symlink),则能正确识别为符号链接;若使用std::filesystem::status对象而非std::filesystem::path,则无论判断顺序如何,都无法识别为符号链接。
行为解释
这个现象的核心是std::filesystem中不同函数对符号链接的处理逻辑差异:
is_regular_file(const path& p):内部默认调用status(p),该函数会自动跟随符号链接,返回链接指向的目标文件的状态。因此对于指向普通文件的符号链接,它会返回true。is_symlink(const path& p):内部默认调用symlink_status(p),该函数不跟随符号链接,直接返回链接本身的状态,因此能正确识别符号链接。
关于判断顺序的影响
当先调用is_regular_file时,它已经跟随链接返回true,后续的else if分支不会执行;而先调用is_symlink时,它直接检查链接本身,返回true,因此会进入对应的分支。
关于status对象的情况
如果先通过status(p)获取状态对象,这个对象存储的是符号链接指向的目标文件的状态(而非链接本身),因此用这个对象调用is_regular_file会返回true,调用is_symlink则返回false。如果想要获取链接本身的状态,应该使用symlink_status(p)。
总结
如果需要判断路径本身是否为符号链接,优先使用is_symlink,或者显式调用symlink_status获取链接自身的状态;如果需要判断链接指向的目标是否为普通文件,使用is_regular_file或status即可。
内容的提问来源于stack exchange,提问作者incubus

