在Unix系统中检查目录是否存在(系统调用)

64

我无法在网上找到解决我的问题的方法。

我想在Unix中调用一个函数,传入一个目录路径,并知道它是否存在。 opendir() 如果目录不存在会返回错误,但是我的目标并不是实际打开目录、检查错误、如果没有错误则关闭它,而只是检查文件是否为目录。

请问有没有什么方便的方式来做到这一点?


1
你为什么在问题中写系统调用?你真的需要一个只能在单个操作系统(Linux,BSD等)上工作的系统调用吗?或者从POSIX C头文件中选择一个POSIX函数(应该适用于任何UNIX发行版)? - Ciro Santilli OurBigBook.com
如果您正在寻找更多不依赖于系统调用的答案,请参阅C++ - 在Linux中确定目录(而不是文件)是否存在 - Roi Danton
5个回答

134

在POSIX系统中有两个相关函数:stat()和lstat()。它们用于确定路径名是否指向您有权访问的实际对象,如果是,则返回的数据告诉您它是什么类型的对象。 stat()lstat()之间的区别在于,如果给定的名称是符号链接,则stat()会跟随符号链接(或链接链,如果它们被链接在一起),并报告链接链末端的对象,而lstat()报告符号链接本身。

#include <sys/stat.h>

struct stat sb;

if (stat(pathname, &sb) == 0 && S_ISDIR(sb.st_mode))
{
    ...it is a directory...
}
如果函数表明操作成功,你可以使用<sys/stat.h>中的S_ISDIR()宏来判断该文件是否是目录。你也可以使用其他的S_IS*宏检查其他文件类型:
  • S_ISDIR — 目录
  • S_ISREG — 普通文件
  • S_ISCHR — 字符设备
  • S_ISBLK — 块设备
  • S_ISFIFO — FIFO(命名管道)
  • S_ISLNK — 符号链接
  • S_ISSOCK — 套接字
(有些系统还提供了一些其他文件类型;例如,Solaris上提供了S_ISDOOR。)

stat 函数期望 pathname 参数为 char 类型? - IgorGanapolsky
4
不是char,而是char *——字符串。函数的POSIX规范链接表明stat()lstat()的第一个参数为const char * restrict pathname - Jonathan Leffler
请点击此处查看有关 S_IS* 宏的更多信息:https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html#Testing-File-Type - m4l490n
来自于 POSIX <sys/stat.h> 规范,如答案中所链接。其中有这样的定义:*S_ISBLK(m) — 测试是否为块特殊文件。* - Jonathan Leffler

5
您可以通过将目录名称作为第一个参数传递给 stat 系统调用来使用它。如果目录存在,则返回 0,否则返回 -1 并设置 errno 为 ENOENT
编辑:
如果返回值是 0,则需要进行额外的检查以确保参数实际上是一个目录,而不是文件/符号链接/字符特殊文件/块特殊文件/FIFO 文件。您可以利用 stat 结构体st_mode 字段来实现这一点。

1
另一种简单的方法是:


int check(unsigned const char type) {
    if(type == DT_REG)
        return 1;
    if(type == DT_DIR)
        return 0;
    return -1;
}

你可以将struct dirent*对象的d_type传递给check函数。
如果check返回1,则该路径指向常规文件。
如果check返回0,则该路径指向目录。
否则,它既不是文件也不是目录(可能是块设备/符号链接等)。

1

如果您不太关心文件系统对象的类型,可以使用access(name,F_OK)检查是否存在具有此名称的内容。 如果您需要确保这是目录,请使用stat()并使用S_ISDIR()宏检查类型。


2
请注意,如果实际UID和有效UID不同(您正在运行setuid程序),则“access()”系统调用将回答与“stat()”不同的问题。具体而言,它检查实际UID(RUID)是否可以访问文件,而所有其他系统调用都使用有效UID(EUID)-包括“opendir()”。 - Jonathan Leffler
当使用 F_OK 标志时,access() 实际上并不检查访问权限,它只检查文件/目录等是否存在。 - blaze
1
但即使使用 F_OK,如果传递给 access() 的名称具有多个路径组件并且当前进程(EUID)没有权限搜索其中一个前导目录组件,则可能会遇到 EACCES(因此它可以检查简单文件名的存在性,但可能会在非简单文件名上遇到问题)。 - Jonathan Leffler
怎么回事?为什么没有人愿意打出POSIX语法来完成这个任务?谁在乎它是如何实现的? - Peter Kionga-Kamau

0

C++17

使用std::filesystem::is_directory

#include <filesystem>

void myFunc(const std::filesystem::path& directoryPath_c)
{
    if (std::filesystem::is_directory(directoryPath_c)) {
    //if (std::filesystem::exists(directoryPath_c)) { // An alternative*
        // do something.
    }
}

或者在它的noexcept版本中:

#include <filesystem>

void myFunc(const std::filesystem::path& directoryPath_c)
{
    std::error_code ec{};
    if (std::filesystem::is_directory(directoryPath_c, ec)) {
    //if (std::filesystem::exists(directoryPath_c, ec)) { // An alternative*
        // do something.
    }
}
  • 替代方案的缺点是,如果directoryPath_c存在但是是一个常规文件,std::filesystem::exists(directoryPath_c)会返回true,尽管该文件夹不存在。

还有std::filesystem::exists() - Remy Lebeau
@RemyLebeau,你是对的。我在std::filesystem::is_directory()中看到的好处是,如果存在同名的普通文件,则std::filesystem::exists的“目录是否存在”的答案将返回'true'。 - Amit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接