这一篇将非常重要,这里介绍的函数可以帮助我们通过写程序来遍历目录。需要重点掌握 4 个函数 opendir、readdir、rewinddir 和 closedir。其实还有两个不怎么重要的函数 seekdir 和 telldir,它似乎没有太大用处,而且很少能用上,感兴趣的同学可以自己查阅 man 手册。
这些函数都是 glibc 提供的函数,也就是说它们并不是系统调用。在学习它们的时候,我们应该拿它和 标准 IO 库中的文件操作的相关函数进行类比(fopen、fread、rewind、fclose、fseek、ftell)。可以说,你几乎不用学习就可以掌握目录操作相关的一系列函数了。
1 opendir
- 函数原型
DIR *opendir(const char *name);
这里的 DIR* 就类似于标准 IO 中的 fopen 函数返回的 FILE*。时刻要提醒自己,目录也是文件。
2 readdir
- 函数原型
struct dirent *readdir(DIR *dirp);
readdir 函数将从当前的目录项偏移的位置开始,读取一条目录项,同时将偏移量增加到下一目录项的位置。
它的返回值 dirent 这个结构体看起来很陌生,如果你是一路读着博文过来,相信你一定能回忆起前面所述的 dir_entry 结构体,它简直无比重要,我已经提及数次。所以无论如何,你也要回头把文件系统相关的知识复习一遍。
只不过 dirent 是在 glibc 中被重新定义,还加入了一个新的字段 d_off 目录项偏移。做了新的一层封装是 glibc 喜欢干的事,它只是帮助我们屏蔽操作系统相关的细节,这一点就像标准IO库中的 fopen、fread 等函数一样。
struct dirent {
ino_t d_ino; // inode number
off_t d_off; // not an offset; see NOTES
unsigned short d_reclen; // length of this record
unsigned char d_type; // type of file; not supported by all filesystem types */
char d_name[256]; // filename
};
3 实验——遍历目录
这段代码实现一个非常简单的功能,就是打印某个目录下所有的目录项(也就是 dir_entry).
- 代码
// traversaldir.c
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <stdio.h>
void traversal(DIR* dir) {
struct dirent* de = NULL;
while((de = readdir(dir)) != NULL) {
printf("%ld\t0x%02x\t%ld\t%s\n", de->d_ino, de->d_type, de->d_off, de->d_name);
}
}
int main(int argc, char* argv[]) {
DIR* dp = opendir(argv[1]);
if (!dp) {
perror("opendir");
return -1;
}
traversal(dp);
closedir(dp);
}
- 编译
$ gcc traversaldir.c -o traversaldir
- 运行
// 遍历当前目录
$ ./traversaldir ./
打印的结果如下(inode 编号、文件类型、偏移量、文件名):
1048944 0x08 65321313 mychdir.c
1050210 0x08 494841770 mymkdir.c
1053420 0x08 724501007 traversaldir.c
1053428 0x08 725159058 myrmdir.c
1050187 0x04 732171668 testdir
1053440 0x08 909641663 traversaldir
1048943 0x04 997909376 .
1050194 0x04 1253739340 ..
1053429 0x08 1569584421 mymkdir
1053421 0x0a 1628563748 hellotest
1053425 0x08 1934677973 mychdir
1053436 0x08 2147483647 myrmdir
实际上我的这个目录现状如下:
drwxrwxr-x 3 allen allen 4096 12月 4 20:41 ./
drwxrwxr-x 10 allen allen 4096 12月 3 12:46 ../
lrwxrwxrwx 1 allen allen 8 12月 4 20:39 hellotest -> testdir//
-rwxrwxr-x 1 allen allen 7460 12月 3 12:42 mychdir*
-rw-rw-r-- 1 allen allen 199 12月 3 12:42 mychdir.c
-rwxrwxr-x 1 allen allen 7384 12月 4 18:02 mymkdir*
-rw-rw-r-- 1 allen allen 203 12月 4 18:02 mymkdir.c
-rwxrwxr-x 1 allen allen 7384 12月 3 22:34 myrmdir*
-rw-rw-r-- 1 allen allen 152 12月 3 22:34 myrmdir.c
drwxrwxr-x 2 allen allen 4096 12月 4 21:15 testdir/
-rwxrwxr-x 1 allen allen 7536 12月 4 20:41 traversaldir*
-rw-rw-r-- 1 allen allen 410 12月 4 20:41 traversaldir.c
4 总结
- 目录也是文件(请原谅我无休止的重复这个知识点)
- 目录文件中是一个个的 dir_entry 结构体
- opendir 函数就像 fopen 函数
- 掌握opendir 和 readdir 函数
- 掌握 rewinddir 函数,虽然本文并未演示,但它很简单,不是吗?
- 记住 closedir
这里留下一个小小的练习:
请递归列出目录下的所有文件,有点类似
ll -R
命令。
这个题目并不容易,你需要考虑可能因为软链接引起的循环。