63

I am not able to find a solution to my problem online.

I would like to call a function in Unix, pass in the path of a directory, and know if it exists. opendir() returns an error if a directory does not exist, but my goal is not to actually open, check the error, close it if no error, but rather just check if a file is a directory or not.

Is there any convenient way to do that please?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jary
  • 733
  • 2
  • 7
  • 9
  • 1
    why did you write system call on the question? do you really want a system call, which will may only work on a single type of OS (linux, BSD, etc), or a POSIX function from the posix c headers (which will should work on any UNIX distro) will do? – Ciro Santilli OurBigBook.com Jun 16 '13 at 15:57
  • If you look for more answers which don't rely on system calls, see [C++ - Determining if directory (not a file) exists in Linux](https://stackoverflow.com/questions/4980815/c-determining-if-directory-not-a-file-exists-in-linux) – Roi Danton Aug 26 '18 at 10:16

5 Answers5

133

There are two relevant functions on POSIX systems: stat() and lstat(). These are used to find out whether a pathname refers to an actual object that you have permission to access, and if so, the data returned tells you what type of object it is. The difference between stat() and lstat() is that if the name you give is a symbolic link, stat() follows the symbolic link (or links if they are chained together) and reports on the object at the end of the chain of links, whereas lstat() reports on the symbolic link itself.

#include <sys/stat.h>

struct stat sb;

if (stat(pathname, &sb) == 0 && S_ISDIR(sb.st_mode))
{
    ...it is a directory...
}

If the function indicates it was successful, you use the S_ISDIR() macro from <sys/stat.h> to determine whether the file is actually a directory.

You can also check for other file types using other S_IS* macros:

  • S_ISDIR — directory
  • S_ISREG — regular file
  • S_ISCHR — character device
  • S_ISBLK — block device
  • S_ISFIFO — FIFO
  • S_ISLNK — symbolic link
  • S_ISSOCK — socket

(Some systems provide a few other file types too; S_ISDOOR is available on Solaris, for example.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • `stat` expects pathname parameter to be a char? – IgorGanapolsky Jun 14 '17 at 15:13
  • 4
    Not a `char` but a `char *` — a string. The links to the POSIX specification for the functions show that the first argument to `stat()` and `lstat()` is `const char * restrict pathname`. – Jonathan Leffler Jun 14 '17 at 15:14
  • check [here](https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html#Testing-File-Type) to see more information about the `S_IS*` macros – m4l490n Jan 31 '20 at 22:31
  • @m4l490n: From the POSIX specification for ``, as linked to in the answer. It has definitions like: *`S_ISBLK(m)` — Test for a block special file.* – Jonathan Leffler Jan 31 '20 at 22:32
5

You can make use of the stat system call by passing it the name of the directory as the first argument. If the directory exists a 0 is returned else -1 is returned and errno will be set to ENOENT

EDIT:

If the return value is 0 you would need an additional check to ensure that the argument is actually a directory and not a file/symlink/char special file/blk special file/FIFO file. You can make use of the st_mode field of the stat structure for this.

codaddict
  • 445,704
  • 82
  • 492
  • 529
1

If you don't really care about type of this filesystem object, access(name, F_OK) checks for exsistence of something with this name. If you need to be sure this is directory, use stat() and check type with S_ISDIR() macro.

blaze
  • 4,326
  • 18
  • 23
  • 2
    Note that if the real and effective UID are not the same (you're running a setuid program), then the `access()` system calls answers a different question from `stat()`. Specifically, it checks whether the real UID (RUID) can access the file, whereas all the other system calls work with the effective UID (EUID) — including `opendir()`. – Jonathan Leffler Aug 03 '16 at 20:36
  • When using F_OK flag, access() actually doesn't check for access rights, it is only checks for file/dir/etc existence. – blaze Aug 04 '16 at 00:45
  • 1
    But even with `F_OK`, you can run into `EACCES` if the name passed to `access()` has multiple path components and the current process (EUID) does not have permission to search one of the leading directory components (so it checks the existence of a simple filename OK, but it may run into problems with a non-simple filename). – Jonathan Leffler Aug 04 '16 at 00:49
  • How? Why is nobody willing to just type out the POSIX syntax to get this done? Who cares how it is implemented? – Peter Kionga-Kamau May 04 '22 at 23:47
1

Another simple way would be:

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

You can then pass struct dirent* object's d_type to check function.

If check returns 1, then that path points to regular file.

If check returns 0, then that path points to a directory.

Otherwise, it is neither a file or a directory (it can be a Block Device/Symbolic Link, etc..)

0

C++17

Use 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.
    }
}

Or in its noexept version:

#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.
    }
}
  • The disadvantage of the alternative is, that if directoryPath_c exists as a regular-file, std::filesystem::exists(directoryPath_c) returns true despite the folder does not exists.
Amit
  • 645
  • 1
  • 3
  • 19
  • There is also [`std::filesystem::exists()`](https://en.cppreference.com/w/cpp/filesystem/exists) – Remy Lebeau May 09 '22 at 17:48
  • @RemyLebeau, you are right. The benefit I see in std::filesystem::is_directory() is that if a regular-file with the same name exists, the answer to "Is directory exists?" of std::filesystem::exists returns 'true'. – Amit May 10 '22 at 06:19