特定のディレクトリ以下、すべてのファイルとディレクトリを削除する

システムコールを使ってファイル操作をするCのプログラムを作成する。

特定のディレクトリ内のファイルとディレクトリをすべて削除したい。unlink()を使えばファイルは消すことができる。また、rmdir()を使えば空のディレクトリは消すことができる。しかし、中味があるディレクトリだと消すことはできないので、中味を消してからディレクトリを消すしか方法はない。なので、階層が深いと面倒な処理が必要になると思われる。

全部考えると複雑なので、できることを組合わせて、最終的な目的を達成できるようにしたい。方針を以下のように定めた。

  • プログラムの方針

最初は、ディレクトリの探索だが、いくつかのサンプルを当たったところ、一番シンプルでエレガントに思えたのは以下の本の3章のprintdir.cというコードだった。カレントディレクトリ以下にあるファイルとディレクトリのリストを表示する基本的なコードだが、なかなか分かりやすい。

  • 参考文献

Linuxプログラミング―例題で学ぶUNIXプログラミング環境のすべて
Linuxプログラミング―例題で学ぶUNIXプログラミング環境のすべてニール マシュー リチャード ストーンズ Neil Matthew

ソフトバンククリエイティブ 2004-08
売り上げランキング : 234188


Amazonで詳しく見る
by G-Tools

再帰的にディレクトリを辿れるようになれば、辿った先にあるファイルとディレクトリを消せばよい。しかし、ディレクトリの中にファイルがあると消したくても消せない。
そこで最初はディレクトリを辿りながらファイルを消す処理を行う。以下の関数でその処理を行う。

void delete_file(char *dir)
{
    DIR *dp;
    struct dirent *ent;
    struct stat statbuf;

    if ((dp = opendir(dir)) == NULL) {
        perror(dir);
        exit(EXIT_FAILURE);
    }
    chdir(dir);
    while ((ent = readdir(dp)) != NULL) {
        lstat(ent->d_name, &statbuf);
        if (S_ISDIR(statbuf.st_mode)) {
            if (strcmp(".", ent->d_name) == 0 ||
                strcmp("..", ent->d_name) == 0)
                continue;
            delete_file(ent->d_name);
        }
        else {
            unlink(ent->d_name);
        }
    }
    chdir("..");
    closedir(dp);
}

ファイルを消した後は、空となったディレクトリを消す。delete_fileと同様にディレクトリを辿りながらディレクトリを消す関数を以下のように書く。

void delete_dir(char *dir)
{
    DIR *dp;
    struct dirent *ent;
    struct stat statbuf;

    if ((dp = opendir(dir)) == NULL) {
        perror(dir);
        exit(EXIT_FAILURE);
    }
    chdir(dir);
    while ((ent = readdir(dp)) != NULL) {
        lstat(ent->d_name, &statbuf);
        if (S_ISDIR(statbuf.st_mode)) {
            if (strcmp(".", ent->d_name) == 0 ||
                strcmp("..", ent->d_name) == 0)
                continue;
            if (rmdir(ent->d_name) < 0) {
                delete_dir(ent->d_name);
            }
        }
    }
    chdir("..");
    closedir(dp);

    if (rmdir(dir) < 0) {
        perror(dir);
        exit(EXIT_FAILURE);
    }
}

delete_fileとdelete_dirは一つにできるかもしれないが、ここでは考えない。時間があるときに検討する。
これらを呼び出すメイン関数は、以下の通り。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>

void delete_file(char *);
void delete_dir(char *);

int main(int argc, char* argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Error: wrong arguments\n");
        exit(EXIT_FAILURE);
    }

    delete_file(argv[1]);
    delete_dir(argv[1]);

    exit(EXIT_SUCCESS);
}

一応動作確認済み(でも動作保障はできかねる)。
ファイルやディレクトリを再帰的に消すようなプログラムのテストは神経を使い非常に疲れる。消すつもりのないディレクトリを消すようなバグがないとも限らない。