C语言 如何删除(POSIX)C中的目录及其内容?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5467725/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 08:12:21  来源:igfitidea点击:

How to delete a directory and its contents in (POSIX) C?

cdirectoryposix

提问by Setjmp

I am most interested in the non-recursive case, but I am guessing others who might track this question would prefer seeing the recursive case.

我对非递归案例最感兴趣,但我猜其他可能会跟踪这个问题的人更喜欢看到递归案例。

Basically, we are aiming to accomplish:

基本上,我们的目标是实现:

rm -rf <target>

However, a system call would be an immature answer.

然而,系统调用将是一个不成熟的答案。

采纳答案by Jonathan Leffler

  1. You need to use nftw()(or possibly ftw()) to traverse the hierarchy.
  2. You need to use unlink()to remove files and other non-directories.
  3. You need to use rmdir()to remove (empty) directories.
  1. 您需要使用nftw()(或可能ftw())来遍历层次结构。
  2. 您需要使用unlink()来删除文件和其他非目录。
  3. 您需要使用rmdir()来删除(空)目录。

You would be better off using nftw()(rather than ftw()) since it gives you controls such as FTW_DEPTHto ensure that all files under a directory are visited before the directory itself is visited.

您最好使用nftw()( 而不是ftw()) ,因为它为您提供了控制,例如FTW_DEPTH确保在访问目录本身之前访问目录下的所有文件。

回答by caf

Use the nftw()(File Tree Walk) function, with the FTW_DEPTHflag. Provide a callback that just calls remove()on the passed file:

使用nftw()带有FTW_DEPTH标志的(File Tree Walk) 函数。提供一个只调用remove()传递文件的回调:

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <ftw.h>
#include <unistd.h>

int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
    int rv = remove(fpath);

    if (rv)
        perror(fpath);

    return rv;
}

int rmrf(char *path)
{
    return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
}

回答by PADYMKO

You can write own implementation command "rm -rf"on pure the C programming language. Source code based only on headers: dirent.h, sys/stat.hand unistd.h. If you need portable code to other systems, as example to the Windows, you need only change headers with corresponding functionality, at the same time the algorithm will not be changed.

您可以在纯 C 编程语言上编写自己的实现命令“rm -rf”。仅基于头文件的源代码:dirent.hsys/stat.hunistd.h。如果您需要将代码移植到其他系统,例如到 Windows,您只需要更改具有相应功能的标题,同时不会更改算法。



A file rmtree.c

一个文件rmtree.c

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

// POSIX dependencies
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>


void
rmtree(const char path[])
{
    size_t path_len;
    char *full_path;
    DIR *dir;
    struct stat stat_path, stat_entry;
    struct dirent *entry;

    // stat for the path
    stat(path, &stat_path);

    // if path does not exists or is not dir - exit with status -1
    if (S_ISDIR(stat_path.st_mode) == 0) {
        fprintf(stderr, "%s: %s\n", "Is not directory", path);
        exit(-1);
    }

    // if not possible to read the directory for this user
    if ((dir = opendir(path)) == NULL) {
        fprintf(stderr, "%s: %s\n", "Can`t open directory", path);
        exit(-1);
    }

    // the length of the path
    path_len = strlen(path);

    // iteration through entries in the directory
    while ((entry = readdir(dir)) != NULL) {

        // skip entries "." and ".."
        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
            continue;

        // determinate a full path of an entry
        full_path = calloc(path_len + strlen(entry->d_name) + 1, sizeof(char));
        strcpy(full_path, path);
        strcat(full_path, "/");
        strcat(full_path, entry->d_name);

        // stat for the entry
        stat(full_path, &stat_entry);

        // recursively remove a nested directory
        if (S_ISDIR(stat_entry.st_mode) != 0) {
            rmtree(full_path);
            continue;
        }

        // remove a file object
        if (unlink(full_path) == 0)
            printf("Removed a file: %s\n", full_path);
        else
            printf("Can`t remove a file: %s\n", full_path);
        free(full_path);
    }

    // remove the devastated directory and close the object of it
    if (rmdir(path) == 0)
        printf("Removed a directory: %s\n", path);
    else
        printf("Can`t remove a directory: %s\n", path);

    closedir(dir);
}


int
main(const int argc, char const *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Missing single operand: path\n");
        return -1;
    }

    rmtree(argv[1]);

    return 0;
}


Examine it.

检查它。

I am use a shell script for generation a file/folder structure.

我使用 shell 脚本生成文件/文件夹结构。

$ cat script.sh 

mkdir -p dir1/{dir1.1,dir1.2,dir1.3}
mkdir -p dir1/dir1.2/{dir1.2.1,dir1.2.2,dir1.2.3}
mkdir -p dir2/{dir2.1,dir2.2}
mkdir -p dir2/dir2.2/dir2.2.1
mkdir -p dir2/dir2.2/{dir2.2.1,dir2.2.2}
mkdir -p dir3/dir3.1
mkdir -p dir4
mkdir -p dir5

touch dir1/dir1.1/file.scala
touch dir1/dir1.2/file.scala
touch dir2/dir2.2/{file.c,file.cpp}
touch dir2/dir2.2/dir2.2.2/{file.go,file.rb}
touch dir3/{file.js,file.java}
touch dir3/dir3.1/{file.c,file.cpp}
> dir4/file.py

Run the script

运行脚本

$ ./script.sh 

Generated the file/folder structure

生成文件/文件夹结构

$ tree
.
├── dir1
│?? ├── dir1.1
│?? │?? └── file.scala
│?? ├── dir1.2
│?? │?? ├── dir1.2.1
│?? │?? ├── dir1.2.2
│?? │?? ├── dir1.2.3
│?? │?? └── file.scala
│?? └── dir1.3
├── dir2
│?? ├── dir2.1
│?? └── dir2.2
│??     ├── dir2.2.1
│??     ├── dir2.2.2
│??     │?? ├── file.go
│??     │?? └── file.rb
│??     ├── file.c
│??     └── file.cpp
├── dir3
│?? ├── dir3.1
│?? │?? ├── file.c
│?? │?? └── file.cpp
│?? ├── file.java
│?? └── file.js
├── dir4
│?? └── file.py
├── dir5
├── rmtree.c
└── script.sh

16 directories, 13 files

Build the source code of the file rmtree.c by the GCC

通过 GCC 构建文件 rmtree.c 的源代码

$ cc -o -Wall -Werror -o rmtree rmtree.c


Remove a directory dir1/dir1.1

删除目录 dir1/dir1.1

$ ./rmtree dir1/dir1.1
Removed a file: dir1/dir1.1/file.scala
Removed a directory: dir1/dir1.1

Remove a directory dir1/dir1.2

删除目录 dir1/dir1.2

$ ./rmtree dir1/dir1.2
Removed a directory: dir1/dir1.2/dir1.2.3
Removed a file: dir1/dir1.2/file.scala
Removed a directory: dir1/dir1.2/dir1.2.1
Removed a directory: dir1/dir1.2/dir1.2.2
Removed a directory: dir1/dir1.2

Remove a directory dir1/

删除目录 dir1/

$ ./rmtree dir1
Removed a directory: dir1/dir1.3
Removed a directory: dir1

Remove a directory dir2/dir2.2/dir2.2.2

删除目录 dir2/dir2.2/dir2.2.2

$ ./rmtree dir2/dir2.2/dir2.2.2
Removed a file: dir2/dir2.2/dir2.2.2/file.rb
Removed a file: dir2/dir2.2/dir2.2.2/file.go
Removed a directory: dir2/dir2.2/dir2.2.2

Remove a directory dir2/

删除目录 dir2/

$ ./rmtree dir2
Removed a directory: dir2/dir2.1
Removed a file: dir2/dir2.2/file.c
Removed a directory: dir2/dir2.2/dir2.2.1
Removed a file: dir2/dir2.2/file.cpp
Removed a directory: dir2/dir2.2
Removed a directory: dir2

Remove a directory dir3/dir3.1

删除目录 dir3/dir3.1

$ ./rmtree dir3/dir3.1
Removed a file: dir3/dir3.1/file.c
Removed a file: dir3/dir3.1/file.cpp
Removed a directory: dir3/dir3.1

Remove a directory dir3

删除目录 dir3

$ ./rmtree dir3
Removed a file: dir3/file.js
Removed a file: dir3/file.java
Removed a directory: dir3

Remove a directory dir4

删除目录 dir4

$ ./rmtree dir4
Removed a file: dir4/file.py
Removed a directory: dir4

Remove a empty directory dir5

删除空目录 dir5

$ ./rmtree dir5
Removed a directory: dir5

If a passed path is not exists or is not path of a directory, you will can see next:

如果传递的路径不存在或不是目录的路径,您将看到下一个:

$ ./rmtree rmtree.c
Is not directory: rmtree.c
$ ./rmtree 11111111111111111
Is not directory: 11111111111111111


See results

查看结果

$ tree
.
├── rmtree
├── rmtree.c
└── script.sh

0 directories, 3 files


Testing environment

测试环境

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.7 (jessie)
Release:    8.7
Codename:   jessie
$ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
$ cc --version
cc (Debian 4.9.2-10) 4.9.2

回答by Setjmp

I just cracked open the GNU rm source and see what exactly it does:

我刚刚打开了 GNU rm 源代码,看看它到底做了什么:

http://www.gnu.org/software/coreutils/

http://www.gnu.org/software/coreutils/

rm relies on the following functions:

rm 依赖于以下函数:

fts_open
fts_read
fts_set
fts_close

which have man pages on both linux and mac.

在 linux 和 mac 上都有手册页。

回答by Dave Goodell

See man 2 unlinkand man 2 rmdirfor system calls that will delete files and (empty) directories respectively. All you need to do then in order to handle the recursive case is to traverse the target directory in a post-order depth-first traversal and delete each entry in that order with the correct deletion routine. You can use opendir, readdir, and closedirto traverse the directory structure.

请参阅man 2 unlinkman 2 rmdir分别删除文件和(空)目录的系统调用。为了处理递归情况,您需要做的就是在后序深度优先遍历中遍历目标目录,并使用正确的删除例程按该顺序删除每个条目。您可以使用opendirreaddirclosedir来遍历目录结构。

回答by Sherm Pendley

In pseudo code, here's the non-recursive approach I would take:

在伪代码中,这是我将采用的非递归方法:

create a stack to hold directory names.
push argv contents onto the stack
while (stack !empty) {
    look at the top directory name on the stack
    for each item in directory {
        if (item is a directoy) {
            push it onto the stack
        } else {
            delete it
        }
    }
    if (no subdirs were pushed) {
        pop the top dir name from the stack
        delete it
    }
}

I'll leave implementing this in C as an exercise for the reader. :-)

我将在 C 中实现它作为读者的练习。:-)

(Edit: Also, unless this is purely a learning exercise, don't reinvent this wheel - it would be far easier, and thus less bug-prone, to use ftw or nftw as others have suggested.)

(编辑:另外,除非这纯粹是一个学习练习,否则不要重新发明这个轮子 - 像其他人建议的那样使用 ftw 或 nftw 会容易得多,因此更不容易出错。)