1. 概述
- 背景:在 Linux I/O 课程中,之前主要学习的是针对文件(设备文件、文本文件)的操作。但在实际场景中,经常需要访问目录(Directory),查看目录下的文件列表。
- 需求:需要专门的接口来处理目录的打开、读取和关闭操作。
- 核心接口:系统提供了关于目录操作的标准接口,主要涉及
opendir、readdir、closedir等函数。
2. 打开目录 (opendir)
- 函数原型:
DIR *opendir(const char *name); - 功能:打开指定路径的目录,返回一个目录流指针。
- 参数:
name:目录路径。- 建议:推荐使用绝对路径,更加严谨和方便;也可以使用相对路径(取决于程序运行场合)。
- 注意路径字符串的大小写。
- 返回值:
- 成功:返回
DIR *指针(目录流指针)。 - 失败:返回
NULL。
- 成功:返回
- 相关接口:
fdopendir:基于已有的文件描述符(FD)打开目录。需先通过open获取 FD,再配合此接口获取目录流指针。一般场景下opendir更常用。
- 头文件:
<dirent.h><sys/types.h>
3. 目录流结构 (DIR)
- 定义:
DIR是一个用于描述打开目录文件的结构体类型。 - 特性:
- 内部细节被屏蔽(Opaque Type),用户无法直接看到其完整结构定义。
- 使用时以指针形式(
DIR *)存在。 - 类似于标准 I/O 中的
FILE *流指针。
- 用途:通过该指针关联已打开的目录,后续操作(如读取、关闭)均依赖此指针。
4. 读取目录项 (readdir)
- 函数原型:
struct dirent *readdir(DIR *dirp); - 功能:从目录流中读取下一个目录项。
- 参数:
dirp:由opendir返回的目录流指针。 - 返回值:
- 成功:返回指向
struct dirent结构体的指针。 - 结束或错误:返回
NULL。
- 成功:返回指向
- 读取机制:
- 每次调用仅返回一个目录项(Entry)。
- 目录内部维护有读取位置指针,每次调用会自动指向下一个项。
- 若要读取整个目录内容,需配合循环(如
while)多次调用,直到返回NULL。
5. 目录项结构 (struct dirent)
- 定义:用于存放读取到的目录中某一个项的信息。
- 主要成员:
ino_t d_ino:文件索引节点号(Inode number)。off_t d_off:指向下一个目录项的偏移量。unsigned char d_type:文件类型(见下文类型判断)。char d_name[256]:文件名(字符串)。
- 文件类型判断 (d_type):
- 通过
d_type成员可以判断当前项是文件、目录还是其他类型。 - 常见宏定义(需在程序中判断):
DT_DIR:目录(Directory)。DT_REG:普通文件(Regular file)。DT_LNK:符号链接(Symbolic link)。- 其他类型(设备文件等)。
- 注意:仅凭
readdir只能获取文件名和基本类型,无法获取权限、时间等详细属性(如需实现ls -l效果,需结合文件属性获取接口)。
- 通过
6. 关闭目录 (closedir)
- 函数原型:
int closedir(DIR *dirp); - 功能:关闭目录流,释放相关资源。
- 参数:
dirp:之前由opendir打开的目录流指针。 - 时机:完成目录读取操作后必须调用。
7. 编程实战示例
- 逻辑流程:
1. 参数检查:判断命令行是否传入目标目录路径。
2. 打开目录:调用opendir,检查返回值是否为NULL。
3. 循环读取:使用while循环调用readdir。
4. 类型判断:通过d_type判断是文件还是目录,并进行不同输出。
5. 关闭目录:循环结束后调用closedir。 - 代码结构示意:
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
DIR *dir = NULL;
struct dirent *ptr = NULL;
// 1. 参数检查
if (argc < 2) {
printf("Usage: %s <directory_path>\n", argv[0]);
return -1;
}
// 2. 打开目录
dir = opendir(argv[1]);
if (dir == NULL) {
perror("opendir failed");
return -1;
}
// 3. 循环读取
while ((ptr = readdir(dir)) != NULL) {
// 4. 类型判断与输出
if (ptr->d_type == DT_DIR) {
printf("[DIR] %s\n", ptr->d_name);
} else if (ptr->d_type == DT_REG) {
printf("[FILE] %s\n", ptr->d_name);
} else {
printf("[OTHER] %s\n", ptr->d_name);
}
}
// 5. 关闭目录
closedir(dir);
return 0;
}
8. 局限性与后续扩展
- 当前能力:
- 可以列出目录下的文件名。
- 可以区分基本的文件类型(目录、普通文件等)。
- 相当于实现了
ls命令的基础功能。
- 当前局限:
- 无法获取文件的详细属性(如权限
rwx、所有者、大小、修改时间等)。 - 无法实现
ls -l的详细列表效果。
- 无法获取文件的详细属性(如权限
- 后续学习:
- 需要学习文件属性获取接口(如
stat系列函数)。 - 结合目录操作与文件属性操作,才能完整实现类似
ls -l的功能。
- 需要学习文件属性获取接口(如