Removing a non empty directory programmatically in C or C++
Asked Answered
P

10

68

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++?

Petra answered 13/2, 2010 at 8:28 Comment(8)
There is no such language as C/C++Griggs
Perhaps it was downvoted because the question has been asked here many times before, for example https://mcmap.net/q/296638/-delete-folder-and-all-files-subdirectories, and people are sick of seeing it? The downvoter wasn't me, BTW.Imprecision
@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.Griggs
@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)Causey
@Causey Not everyone can vote to close, but most can downvote.Imprecision
@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 dupCausey
@Causey 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.Imprecision
@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.Church
J
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;
}
Jaban answered 13/2, 2010 at 8:41 Comment(7)
reasonable answer but "easily run out of stackspace"?Revisory
@Revisory Yes. I have seen it happen. It's also easier on Windows than POSIX, because WIN32_FIND_DATA is huge, whereas DIR * and struct dirent * are just two pointers..Jaban
First time I ever did this, I didn't check for "..". And yes, the result is that the program changes directory all the way to c:, and then starts deleting from there! "Luckily", it happened at work. : )Suiting
Looks like a nice solution, but I included sys/stat.h at the top, but still cannot use the undefined DIR typedef.Raguelragweed
@Viz you want dirent.hJaban
I opted to use lstat() too as you may not want to delete the file a symlink points to.Lacagnia
@GrimmTheOpiner "Luckily" it wasn't a RESTful application, or we'd be playing with sticks and stones right now.Catastrophism
I
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;
}
Inhibit answered 6/1, 2015 at 22:45 Comment(6)
Very nice example code and explanation - this should be the accepted answer.Plat
The fts interface is not available in musl libc.Chagres
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 ?Devoice
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.Beguile
@Tony Yes. errno should hold the error code. (I updated the answer to fix the bug you found.)Slender
@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. sourceware.org/bugzilla/show_bug.cgi?id=11460. This occurred back in 2016.Stature
A
24

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;
}
Ahearn answered 23/3, 2017 at 14:10 Comment(2)
I'm glad someone mentioned about ntfw. :)Dextroamphetamine
Keep in mind that if you are targeting Windows (yuq!), nftw isn't available, even though Windows is technically POSIX compliant.Veratrine
C
16

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.

Causey answered 13/2, 2010 at 8:45 Comment(2)
brb downloading Boost.Filesystem library on my virtualbox DOS to use on my turbo c compiler.Extraterritorial
Why load a massive library, just to do this?Seismography
P
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;
}
Phenosafranine answered 26/4, 2018 at 20:43 Comment(0)
B
1

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

Blackhead answered 13/2, 2010 at 8:32 Comment(0)
E
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;
}
Eire answered 3/1, 2017 at 22:15 Comment(1)
Surrounding your code with an explanation would seriously improve your answer.Seaddon
F
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
    }
Floorer answered 2/3, 2019 at 8:39 Comment(3)
int main, pleaseSeismography
@Seismography Is there a difference?Fluid
@user16217248 Of course there is! int main() is portable. void main() has implementation-defined behavior. Also, see https://mcmap.net/q/296639/-int-main-vs-void-main-in-c-duplicate.Seismography
G
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 ([email protected])
 * @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;
}
Gibberish answered 31/5, 2022 at 5:35 Comment(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.Sheepfold
R
0

For windows, may not handle hidden or system files properly, but (Linux may have a similar command...)

#include <stdlib.h>
int     system_run_status;
char    command_line[250] = "";
    sprintf(command_line, "rmdir /s /q %s", dir_to_remove);
    system_run_status = system(command_line);
Roundelay answered 9/12, 2023 at 7:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.