一、简介
Linux 下可以使用 rmdir 系统调用来删除一个目录,但是只能删除一个非空目录。
NAME
rmdir - delete a directory
SYNOPSIS
#include <unistd.h>
int rmdir(const char *pathname);
DESCRIPTION
rmdir() deletes a directory, which must be empty.
在 shell命令行我们用 rm -rf 来删除一个目录,那我们仿照 rm -rf 的原理来实现一个使用C语言删除一个非空的目录。
当执行 rm -rf 命令时,它会按照以下步骤进行操作:
首先,命令会尝试打开指定的目录 <directory>。
如果成功打开目录,命令会遍历目录中的所有子目录和文件。
对于每个子目录和文件,命令会递归调用自身,以便删除子目录和文件。
如果遇到子目录,将按照相同的步骤递归删除子目录及其内容。
如果遇到文件,命令将直接删除文件。
删除完成后,命令关闭目录。
(1)使用 tree 命令查看目录结构:
# tree dir_test/
dir_test/
├── 3.txt
├── 4.txt
├── 5.txt
└── dir1
├── 1.txt
└── 2.txt
1 directory, 5 files
(2)使用 strace 来 追踪 rm -rf 命令:
[root@localhost c_test]# strace -e trace=file rm -rf dir_test/
execve("/usr/bin/rm", ["rm", "-rf", "dir_test/"], 0x7ffe2a252c50 /* 27 vars */) = 0
......
newfstatat(AT_FDCWD, "dir_test/", {st_mode=S_IFDIR|0755, st_size=57, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "dir_test/", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = 3
openat(AT_FDCWD, "dir_test/", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = 3
newfstatat(4, "dir1", {st_mode=S_IFDIR|0755, st_size=32, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(4, "dir1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = 3
openat(4, "dir1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = 3
unlinkat(5, "1.txt", 0) = 0
unlinkat(5, "2.txt", 0) = 0
unlinkat(4, "dir1", AT_REMOVEDIR) = 0
unlinkat(4, "3.txt", 0) = 0
unlinkat(4, "4.txt", 0) = 0
unlinkat(4, "5.txt", 0) = 0
unlinkat(AT_FDCWD, "dir_test/", AT_REMOVEDIR) = 0
主要是:
unlinkat(5, "1.txt", 0) = 0
unlinkat(5, "2.txt", 0) = 0
unlinkat(4, "dir1", AT_REMOVEDIR) = 0
unlinkat(4, "3.txt", 0) = 0
unlinkat(4, "4.txt", 0) = 0
unlinkat(4, "5.txt", 0) = 0
unlinkat(AT_FDCWD, "dir_test/", AT_REMOVEDIR) = 0
可以看到删除一个目录,首先将该目录下的文件删除掉,然后再删除掉该空目录。
二、代码演示
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <limits.h>
void removeDirectory(const char* dirPath) {
struct stat st;
if (lstat(dirPath, &st) == -1) {
perror("lstat");
return;
}
if (S_ISDIR(st.st_mode)) {
DIR* dir = opendir(dirPath);
if (dir == NULL) {
perror("opendir");
return;
}
char path[PATH_MAX];
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s", dirPath, entry->d_name);
removeDirectory(path);
}
closedir(dir);
if (rmdir(dirPath) == -1) {
perror("rmdir");
return;
}
printf("Directory %s deleted successfully.\n", dirPath);
} else if (S_ISREG(st.st_mode)) {
if (remove(dirPath) == -1) {
perror("remove");
return;
}
printf("File %s deleted successfully.\n", dirPath);
} else {
printf("%s is of unknown file type.\n", dirPath);
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: %s <file_path>\n", argv[0]);
return 1;
}
const char* filePath = argv[1];
removeDirectory(filePath);
return 0;
}
# ./a.out dir_test/
File dir_test//dir1/1.txt deleted successfully.
File dir_test//dir1/2.txt deleted successfully.
Directory dir_test//dir1 deleted successfully.
File dir_test//3.txt deleted successfully.
File dir_test//4.txt deleted successfully.
File dir_test//5.txt deleted successfully.
Directory dir_test/ deleted successfully.
unlinkat(5, "1.txt", 0) = 0
unlinkat(5, "2.txt", 0) = 0
unlinkat(4, "dir1", AT_REMOVEDIR) = 0
unlinkat(4, "3.txt", 0) = 0
unlinkat(4, "4.txt", 0) = 0
unlinkat(4, "5.txt", 0) = 0
unlinkat(AT_FDCWD, "dir_test/", AT_REMOVEDIR) = 0
可以看到和 rm -rf 命令删除非空目录过程相同。