25

How would I determine if a directory (not a file) existed using C++ in Linux? I tried using the stat() function but it returned positive when a file was found. I only want to find if the inputted string is a directory, not something else.

daxvena
  • 1,140
  • 3
  • 14
  • 30

6 Answers6

31

According to man(2) stat you can use the S_ISDIR macro on the st_mode field:

bool isdir = S_ISDIR(st.st_mode);

Side note, I would recommend using Boost and/or Qt4 to make cross-platform support easier if your software can be viable on other OSs.

OneOfOne
  • 95,033
  • 20
  • 184
  • 185
20

how about something i found here

#include <dirent.h>

bool DirectoryExists( const char* pzPath )
{
    if ( pzPath == NULL) return false;

    DIR *pDir;
    bool bExists = false;

    pDir = opendir (pzPath);

    if (pDir != NULL)
    {
        bExists = true;    
        (void) closedir (pDir);
    }

    return bExists;
}

Or using stat

struct stat st;
if(stat("/tmp",&st) == 0)
    if(st.st_mode & S_IFDIR != 0)
        printf(" /tmp is present\n");
poy
  • 10,063
  • 9
  • 49
  • 74
ayush
  • 14,350
  • 11
  • 53
  • 100
  • It may not be that bad but the example given above is not really that efficient and the bottom example is what I am using already, except that I am using != instead of == – daxvena Feb 12 '11 at 21:53
  • 3
    ya the bottom one needs to be updated with DarkDust's extra `and` condition. `(myStat.st_mode) & S_IFMT) == S_IFDIR)`. thanks DarkDust. – ayush Feb 12 '11 at 22:01
  • The example given above is the only one working for me so I am going to accept it as the answer for now. – daxvena Feb 12 '11 at 22:56
  • It is not necessary to try opendir on the directory; use the S_ISDIR macro. – MarkR Feb 13 '11 at 08:23
  • Are you saying that if the dir exists it should be if(st.st_mode & S_IFDIR == 0) ??? – Michele Oct 20 '16 at 17:04
10

If you can check out the boost filesystem library. It's a great way to deal with this kind of problems in a generic and portable manner.

In this case it would suffice to use:

#include "boost/filesystem.hpp"   
using namespace boost::filesystem; 
...
if ( !exists( "test/mydir" ) ) {bla bla}
Dr G
  • 3,987
  • 2
  • 19
  • 25
7

The way I understand your question is this: you have a path, say, /foo/bar/baz (baz is a file) and you want to know whether /foo/bar exists. If so, the solution looks something like this (untested):

char *myDir = dirname(myPath);
struct stat myStat;
if ((stat(myDir, &myStat) == 0) && (((myStat.st_mode) & S_IFMT) == S_IFDIR)) {
    // myDir exists and is a directory.
}
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • No, what I mean is "/foo/bar/baz" and "/foo/bar/baz" use the same would be considered as the same string but one baz is a directory and another is a file. Using stat() it only tells me if the string exists, not if it's a file or directory. – daxvena Feb 12 '11 at 21:57
  • How could that ever happen? You can't have two different directory entries with the same name; either `/foo/bar/baz` doesn't exist, or it exists and is a directory, or it exists and is not a directory; it can't exist as *both* a directory and not a directory. – John Bartholomew Feb 12 '11 at 21:59
  • Ah, so you want to know which it is. In that case, OneOfOne's answer is what you want. – John Bartholomew Feb 12 '11 at 22:01
  • This is also a correct answer except that the use of `st_mode & S_IFMT` instead of `S_ISDIR` is poor style. – zwol Feb 12 '11 at 22:03
  • @MetaDark: stat *does* tell you whether the path is a directory, in the `st_mode` flags. Either use my code to check for the directory flag or the `S_ISDIR` macro, as seen in the answer of OneOfOne. – DarkDust Feb 12 '11 at 22:11
  • I love c++, this is intuitive and so easy to remember and it works! – Andrzej Rehmann Mar 11 '14 at 06:45
4

In C++17**, std::filesystem provides two variants to determine the existence of a path:

  1. is_directory() determines, if a path is a directory and does exist in the actual filesystem
  2. exists() just determines, if the path exists in the actual filesystem (not checking, if it is a directory)

Example (without error handling):

#include <iostream>
#include <filesystem> // C++17
//#include <experimental/filesystem> // C++14
namespace fs = std::filesystem;
//namespace fs = std::experimental::filesystem; // C++14

int main()
{
    // Prepare.
    const auto processWorkingDir = fs::current_path();
    const auto existingDir = processWorkingDir / "existing/directory"; // Should exist in file system.
    const auto notExistingDir = processWorkingDir / "fake/path";
    const auto file = processWorkingDir / "file.ext"; // Should exist in file system.

    // Test.
    std::cout
        << "existing dir:\t" << fs::is_directory(existingDir) << "\n"
        << "fake dir:\t" << fs::is_directory(notExistingDir) << "\n"
        << "existing file:\t" << fs::is_directory(file) << "\n\n";

    std::cout
        << "existing dir:\t" << fs::exists(existingDir) << "\n"
        << "fake dir:\t" << fs::exists(notExistingDir) << "\n"
        << "existing file:\t" << fs::exists(file);
}

Possible output:

existing dir:   1
fake dir:       0
existing file:  0

existing dir:   1
fake dir:       0
existing file:  1

**in C++14 std::experimental::filesystem is available


Both functions throw filesystem_error in case of errors. If you want to avoid catching exceptions, use the overloaded variants with std::error_code as second parameter.

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

bool isExistingDir(const fs::path& p) noexcept
{
    try
    {
        return fs::is_directory(p);
    }
    catch (std::exception& e)
    {
        // Output the error message.
        const auto theError = std::string{ e.what() };
        std::cerr << theError;

        return false;
    }
}

bool isExistingDirEC(const fs::path& p) noexcept
{
    std::error_code ec;
    const auto isDir = fs::is_directory(p, ec);
    if (ec)
    {
        // Output the error message.
        const auto theError = ec.message();
        std::cerr << theError;

        return false;
    }
    else
    {
        return isDir;
    }
}

int main()
{
    const auto notExistingPath = fs::path{ "\xa0\xa1" };
    isExistingDir(notExistingPath);
    isExistingDirEC(notExistingPath);
}
Roi Danton
  • 7,933
  • 6
  • 68
  • 80
  • 1
    Why use `is_directory()` first and then `exists()` second? Also, is `exists()` really needed? – Andrew Aug 26 '18 at 01:55
  • On Windows, I did: `if (!is_directory("myDir")) { create_directory("myDir"); if (!is_directory("myDir")) { return false; } }` and it worked just fine (created the folder if it didn't exist, didn't if it did exist as a folder, returned false if it existed as a file). – Andrew Aug 26 '18 at 02:00
  • 1
    @Andrew Thanks for the hint, `exists()` was redundant indeed! I've updated the example. Since the question does not include creating the directory, I'm omitting that in the answer. – Roi Danton Aug 26 '18 at 09:57
1

If you want to find out whether a directory exists because you want to do something with it if it does (create a file/directory inside, scan its contents, etc) you should just go ahead and do whatever you want to do, then check whether it failed, and if so, report strerror(errno) to the user. This is a general principle of programming under Unix: don't try to figure out whether the thing you want to do will work. Attempt it, then see if it failed.

If you want to behave specially if whatever-it-was failed because a directory didn't exist (for instance, if you want to create a file and all necessary containing directories) you check for errno == ENOENT after open fails.

I see that one responder has recommended the use of boost::filesystem. I would like to endorse this recommendation, but sadly I cannot, because boost::filesystem is not header-only, and all of Boost's non-header-only modules have a horrible track record of causing mysterious breakage if you upgrade the shared library without recompiling the app, or even if you just didn't manage to compile your app with exactly the same flags used to compile the shared library. The maintenance grief is just not worth it.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    If it detects it as it file, it continues anyway and creates a seemingly corrupt tar file. Oh yeah if I haven't said it already I am trying to tar the directory. – daxvena Feb 12 '11 at 22:06
  • 2
    The first step in creating an archive from a directory is scanning the directory, so why can't you just call `opendir` and see if it fails (which it should when applied to a plain file)? – zwol Feb 12 '11 at 22:09