【发布时间】:2011-07-24 23:23:25
【问题描述】:
我对非递归案例最感兴趣,但我猜其他可能跟踪此问题的人更愿意看到递归案例。
基本上,我们的目标是:
rm -rf <target>
但是,系统调用将是一个不成熟的答案。
【问题讨论】:
我对非递归案例最感兴趣,但我猜其他可能跟踪此问题的人更愿意看到递归案例。
基本上,我们的目标是:
rm -rf <target>
但是,系统调用将是一个不成熟的答案。
【问题讨论】:
请参阅man 2 unlink 和man 2 rmdir,了解将分别删除文件和(空)目录的系统调用。为了处理递归情况,您需要做的就是以后序深度优先遍历的方式遍历目标目录,并使用正确的删除例程按该顺序删除每个条目。可以使用opendir、readdir、closedir遍历目录结构。
【讨论】:
【讨论】:
nftw,这让我不以为然。确保将nftw 与FTW_DEPTH 选项一起使用,否则您将获得预购遍历,这不适用于rmdir-ing 目录。
nftw() 和 FTW_DEPTH 的评论。我同意你的看法。
ntfw() 的系统也具有remove(),当在目录上调用时其行为类似于rmdir(),在路径上调用时类似于unlink()。所以总是使用remov() 并使用ntfw 和FTW_DEPTH 就可以了。
ntfw() 绝对不是线程安全的,如果其他线程更改当前工作目录,它甚至可能会中断 - 一个好的实现可能不会关心但 POSIX 标准说ntfw() 可以依赖设置的工作目录在处理过程中保持设置,并且一些简单的实现可能会这样做。
使用带有FTW_DEPTH 标志的nftw()(文件树遍历)功能。提供一个只在传递的文件上调用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);
}
【讨论】:
remove 可以删除目录。
remove() 只会删除 blank 目录。
nftw() 的FWD_DEPTH 标志意味着在将目录本身传递给remove() 之前将删除目录的内容,因此它将为空那一点。
rm -rf成功的一种情况下失败了:如果有很多嵌套目录,remove会遇到ENAMETOOLONG错误,但rm -rf会使用openat 和unlinkat 一次使用一个目录名称,甚至删除嵌套很深的目录结构。
nnftw()(新的、新的文件树遍历)来传递目录文件描述符和相对名称:)
在伪代码中,这是我将采用的非递归方法:
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
}
}
我将把在 C 中实现这个作为练习留给读者。 :-)
(编辑:另外,除非这纯粹是一个学习练习,否则不要重新发明这个轮子 - 正如其他人建议的那样,使用 ftw 或 nftw 会容易得多,因此更不容易出错。)
【讨论】:
我刚刚破解了 GNU rm 源代码,看看它到底做了什么:
http://www.gnu.org/software/coreutils/
rm 依赖于以下函数:
fts_open
fts_read
fts_set
fts_close
在 linux 和 mac 上都有手册页。
【讨论】:
nftw 和 ftw 是更高级别的函数,因为它们基于 fts_ 系列函数。
您可以在纯C编程语言上编写自己的实现命令“rm -rf”。仅基于标头的源代码:dirent.h、sys/stat.h 和 unistd.h。如果您需要将代码移植到其他系统,例如Windows,您只需要更改具有相应功能的标题,同时算法不会改变。
一个文件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;
}
我正在使用 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
运行脚本
$ ./script.sh
生成文件/文件夹结构
$ 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
通过 GCC 构建文件 rmtree.c 的源代码
$ cc -o -Wall -Werror -o rmtree rmtree.c
删除目录 dir1/dir1.1
$ ./rmtree dir1/dir1.1
Removed a file: dir1/dir1.1/file.scala
Removed a directory: dir1/dir1.1
删除目录 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
删除目录 dir1/
$ ./rmtree dir1
Removed a directory: dir1/dir1.3
Removed a directory: dir1
删除目录 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
删除目录 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
删除目录 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
删除目录 dir3
$ ./rmtree dir3
Removed a file: dir3/file.js
Removed a file: dir3/file.java
Removed a directory: dir3
删除目录 dir4
$ ./rmtree dir4
Removed a file: dir4/file.py
Removed a directory: dir4
删除一个空目录dir5
$ ./rmtree dir5
Removed a directory: dir5
如果传递的路径不存在或者不是目录的路径,你会看到下一个:
$ ./rmtree rmtree.c
Is not directory: rmtree.c
$ ./rmtree 11111111111111111
Is not directory: 11111111111111111
查看结果
$ tree
.
├── rmtree
├── rmtree.c
└── script.sh
0 directories, 3 files
测试环境
$ 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
【讨论】:
full_path = calloc(path_len + strlen(entry->d_name) + 1, sizeof(char));释放它怎么样?