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
How to delete a directory and its contents in (POSIX) C?
提问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
- You need to use
nftw()(or possiblyftw()) to traverse the hierarchy. - You need to use
unlink()to remove files and other non-directories. - You need to use
rmdir()to remove (empty) directories.
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.h、sys/stat.h和unistd.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 unlink和man 2 rmdir分别删除文件和(空)目录的系统调用。为了处理递归情况,您需要做的就是在后序深度优先遍历中遍历目标目录,并使用正确的删除例程按该顺序删除每个条目。您可以使用opendir、readdir和closedir来遍历目录结构。
回答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 会容易得多,因此更不容易出错。)

