66

How to delete a non empty directory in C or C++? Is there any function? rmdir only deletes empty directory. Please provide a way without using any external library.

Also tell me how to delete a file in C or C++?

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
avd
  • 13,993
  • 32
  • 78
  • 99
  • 10
    There is no such language as C/C++ – Benoît Feb 13 '10 at 10:26
  • 1
    Perhaps it was downvoted because the question has been asked here many times before, for example http://stackoverflow.com/questions/1149764/delete-folder-with-items, and people are sick of seeing it? The downvoter wasn't me, BTW. –  Feb 13 '10 at 10:44
  • @peterchen : no, but it's the second time today lex asks about c/c++. And i guess i've seen too many resumes where people pretend they know this infamous language c/c++ which does not exist. – Benoît Feb 13 '10 at 11:34
  • 2
    @Neil - I thought the standard procedure in case of duplication was to leave a comment, not to downvote? (I know it wasn't you who downvoted, but you seem to imply that it's normal that others might do it) – Manuel Feb 13 '10 at 12:32
  • @Manuel Not everyone can vote to close, but most can downvote. –  Feb 13 '10 at 12:50
  • @Neil - But everyone can leave a comment, and IMO that would me more productive than downvoting. For example, I wouldn't have answered this particular question if I had known it was a dup – Manuel Feb 13 '10 at 12:59
  • @Manuel Different folks have different ideas about how to use SO - there is no "right" way. If you want to discuss this further (not with me), or read lots of questions on the topic, got to Meta - link at bottom of this page. –  Feb 13 '10 at 13:13
  • @Benoît Here at stackoverflow, they don't know the difference between c and c++. To them, they are the same. I tried to correct a bunch of listed many years ago, and was told to stop by the unthinking mods. – Deanie Jan 19 '23 at 17:03

9 Answers9

36

You want to write a function (a recursive function is easiest, but can easily run out of stack space on deep directories) that will enumerate the children of a directory. If you find a child that is a directory, you recurse on that. Otherwise, you delete the files inside. When you are done, the directory is empty and you can remove it via the syscall.

To enumerate directories on Unix, you can use opendir(), readdir(), and closedir(). To remove you use rmdir() on an empty directory (i.e. at the end of your function, after deleting the children) and unlink() on a file. Note that on many systems the d_type member in struct dirent is not supported; on these platforms, you will have to use stat() and S_ISDIR(stat.st_mode) to determine if a given path is a directory.

On Windows, you will use FindFirstFile()/FindNextFile() to enumerate, RemoveDirectory() on empty directories, and DeleteFile() to remove files.

Here's an example that might work on Unix (completely untested):

int remove_directory(const char *path) {
   DIR *d = opendir(path);
   size_t path_len = strlen(path);
   int r = -1;

   if (d) {
      struct dirent *p;

      r = 0;
      while (!r && (p=readdir(d))) {
          int r2 = -1;
          char *buf;
          size_t len;

          /* Skip the names "." and ".." as we don't want to recurse on them. */
          if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
             continue;

          len = path_len + strlen(p->d_name) + 2; 
          buf = malloc(len);

          if (buf) {
             struct stat statbuf;

             snprintf(buf, len, "%s/%s", path, p->d_name);
             if (!stat(buf, &statbuf)) {
                if (S_ISDIR(statbuf.st_mode))
                   r2 = remove_directory(buf);
                else
                   r2 = unlink(buf);
             }
             free(buf);
          }
          r = r2;
      }
      closedir(d);
   }

   if (!r)
      r = rmdir(path);

   return r;
}
pevik
  • 4,523
  • 3
  • 33
  • 44
asveikau
  • 39,039
  • 2
  • 53
  • 68
24

Many unix-like systems (Linux, the BSDs, and OS X, at the very least) have the fts functions for directory traversal.

To recursively delete a directory, perform a depth-first traversal (without following symlinks) and remove every visited file:

int recursive_delete(const char *dir)
{
    int ret = 0;
    FTS *ftsp = NULL;
    FTSENT *curr;

    // Cast needed (in C) because fts_open() takes a "char * const *", instead
    // of a "const char * const *", which is only allowed in C++. fts_open()
    // does not modify the argument.
    char *files[] = { (char *) dir, NULL };

    // FTS_NOCHDIR  - Avoid changing cwd, which could cause unexpected behavior
    //                in multithreaded programs
    // FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside
    //                of the specified directory
    // FTS_XDEV     - Don't cross filesystem boundaries
    ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
    if (!ftsp) {
        fprintf(stderr, "%s: fts_open failed: %s\n", dir, strerror(errno));
        ret = -1;
        goto finish;
    }

    while ((curr = fts_read(ftsp))) {
        switch (curr->fts_info) {
        case FTS_NS:
        case FTS_DNR:
        case FTS_ERR:
            fprintf(stderr, "%s: fts_read error: %s\n",
                    curr->fts_accpath, strerror(curr->fts_errno));
            break;

        case FTS_DC:
        case FTS_DOT:
        case FTS_NSOK:
            // Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were
            // passed to fts_open()
            break;

        case FTS_D:
            // Do nothing. Need depth-first search, so directories are deleted
            // in FTS_DP
            break;

        case FTS_DP:
        case FTS_F:
        case FTS_SL:
        case FTS_SLNONE:
        case FTS_DEFAULT:
            if (remove(curr->fts_accpath) < 0) {
                fprintf(stderr, "%s: Failed to remove: %s\n",
                        curr->fts_path, strerror(curr->fts_errno));
                ret = -1;
            }
            break;
        }
    }

finish:
    if (ftsp) {
        fts_close(ftsp);
    }

    return ret;
}
strager
  • 88,763
  • 26
  • 134
  • 176
Andrew Gunnerson
  • 638
  • 1
  • 8
  • 17
  • 2
    Very nice example code and explanation - this should be the accepted answer. – Paul R Sep 13 '16 at 16:31
  • The `fts` interface [is not available in musl libc](http://wiki.musl-libc.org/wiki/FAQ#Q:_why_is_fts.h_not_included_.3F). – Jonathon Reinhart May 31 '17 at 10:58
  • Is this `fts`-based solution less prone to the potential stack/heap overflow problems of solutions that iteratively/recursively use `opendir`/`readdir` like asveikau's ? – StoneThrow May 17 '19 at 17:21
  • Using this iterative method, a stack is not required (in user program) for recording ancestor directories, so I think it is not likely to suffer from overflow programs. – Andy Pan Aug 03 '19 at 12:35
  • @Tony Yes. errno should hold the error code. (I updated the answer to fix the bug you found.) – strager Jan 12 '21 at 20:24
  • @JonathonReinhart, the logic specified by musl for not including the interface seems outdated. From what I can tell the relevant issue in GCC has been marked as `RESOLVED FIXED`. https://sourceware.org/bugzilla/show_bug.cgi?id=11460. This occurred back in 2016. – tristan957 Nov 30 '22 at 19:20
22

If you are using a POSIX compliant OS, you could use nftw() for file tree traversal and remove (removes files or directories). If you are in C++ and your project uses boost, it is not a bad idea to use the Boost.Filesystem as suggested by Manuel.

In the code example below I decided not to traverse symbolic links and mount points (just to avoid a grand removal:) ):

#include <stdio.h>
#include <stdlib.h>
#include <ftw.h>

static int rmFiles(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb)
{
    if(remove(pathname) < 0)
    {
        perror("ERROR: remove");
        return -1;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr,"usage: %s path\n",argv[0]);
        exit(1);
    }

    // Delete the directory and its contents by traversing the tree in reverse order, without crossing mount boundaries and symbolic links

    if (nftw(argv[1], rmFiles,10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
    {
        perror("ERROR: ntfw");
        exit(1);
    }

    return 0;
}
JonnyBoy
  • 1,555
  • 14
  • 24
marcmagransdeabril
  • 1,445
  • 16
  • 27
14

The easiest way to do this is with remove_all function of the Boost.Filesystem library. Besides, the resulting code will be portable.

If you want to write something specific for Unix (rmdir) or for Windows (RemoveDirectory) then you'll have to write a function that deletes are subfiles and subfolders recursively.

EDIT

Looks like this question was already asked, in fact someone already recommended Boost's remove_all. So please don't upvote my answer.

Community
  • 1
  • 1
Manuel
  • 12,749
  • 1
  • 27
  • 35
7

C++17 has <experimental\filesystem> which is based on the boost version.

Use std::experimental::filesystem::remove_all to remove recursively.

If you need more control, try std::experimental::filesystem::recursive_directory_iterator.

You can also write your own recursion with the non-resursive version of the iterator.

namespace fs = std::experimental::filesystem;
void IterateRecursively(fs::path path)
{
  if (fs::is_directory(path))
  {
    for (auto & child : fs::directory_iterator(path))
      IterateRecursively(child.path());
  }

  std::cout << path << std::endl;
}
greedy52
  • 1,345
  • 9
  • 8
1

You can use opendir and readdir to read directory entries and unlink to delete them.

diciu
  • 29,133
  • 4
  • 51
  • 68
0
//======================================================
// Recursely Delete files using:
//   Gnome-Glib & C++11
//======================================================

#include <iostream>
#include <string>
#include <glib.h>
#include <glib/gstdio.h>

using namespace std;

int DirDelete(const string& path)
{
   const gchar*    p;
   GError*   gerr;
   GDir*     d;
   int       r;
   string    ps;
   string    path_i;
   cout << "open:" << path << "\n";
   d        = g_dir_open(path.c_str(), 0, &gerr);
   r        = -1;

   if (d) {
      r = 0;

      while (!r && (p=g_dir_read_name(d))) {
          ps = string{p};
          if (ps == "." || ps == "..") {
            continue;
          }

          path_i = path + string{"/"} + p;


          if (g_file_test(path_i.c_str(), G_FILE_TEST_IS_DIR) != 0) {
            cout << "recurse:" << path_i << "\n";
            r = DirDelete(path_i);
          }
          else {
            cout << "unlink:" << path_i << "\n";
            r = g_unlink(path_i.c_str());
          }
      }

      g_dir_close(d);
   }

   if (r == 0) {
      r = g_rmdir(path.c_str());
     cout << "rmdir:" << path << "\n";

   }

   return r;
}
Bimo
  • 5,987
  • 2
  • 39
  • 61
0

How to delete a non empty folder using unlinkat() in c?

Here is my work on it:

    /*
     * Program to erase the files/subfolders in a directory given as an input
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dirent.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    void remove_dir_content(const char *path)
    {
        struct dirent *de;
        char fname[300];
        DIR *dr = opendir(path);
        if(dr == NULL)
        {
            printf("No file or directory found\n");
            return;
        }
        while((de = readdir(dr)) != NULL)
        {
            int ret = -1;
            struct stat statbuf;
            sprintf(fname,"%s/%s",path,de->d_name);
            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
                        continue;
            if(!stat(fname, &statbuf))
            {
                if(S_ISDIR(statbuf.st_mode))
                {
                    printf("Is dir: %s\n",fname);
                    printf("Err: %d\n",ret = unlinkat(dirfd(dr),fname,AT_REMOVEDIR));
                    if(ret != 0)
                    {
                        remove_dir_content(fname);
                        printf("Err: %d\n",ret = unlinkat(dirfd(dr),fname,AT_REMOVEDIR));
                    }
                }
                else
                {
                    printf("Is file: %s\n",fname);
                    printf("Err: %d\n",unlink(fname));
                }
            }
        }
        closedir(dr);
    }
    void main()
    {
        char str[10],str1[20] = "../",fname[300]; // Use str,str1 as your directory path where it's files & subfolders will be deleted.
        printf("Enter the dirctory name: ");
        scanf("%s",str);
        strcat(str1,str);
        printf("str1: %s\n",str1);
        remove_dir_content(str1); //str1 indicates the directory path
    }
0

This code will open particular directory and iterate over all files and delete them which are under that directory. After that it will delete empty directory at the end.

/**
 * @file RemoveDir.c
 * @author Om Patel (ompatel1861@gmail.com)
 * @brief This program will remove non empty directory.  
 * @version 0.1
 * @date 2022-05-31
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include<stdio.h>
#include<string.h>
#include<dirent.h>
#include <unistd.h>

int main()
{
    DIR* dir = opendir("OUTPUT/Aakash");
    struct dirent* entity;
    entity = readdir(dir);
    while(entity != NULL){
        char path[30] ="OUTPUT/Aakash/";
        printf("%s\n",entity->d_name);
        strcat(path,entity->d_name);
        printf("PAth: %s\n",path);
        remove(path);
        entity = readdir(dir);
    }
    char path1[30] ="OUTPUT/Aakash";
    rmdir(path1);
    closedir(dir);
    char out[20]="OUTPUT/";
                char fol_file[30];
            sprintf(fol_file,"%s\\",out);
            printf("%s",fol_file);
    return 0;
}
OM PATEL
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 31 '22 at 06:17