如何修复C语言目录遍历中errno无法识别所有文件的问题?
遍历目录时移除注释条件后出现errno=2和errno=20的问题,如何修复?
我正在写一个C程序,功能是传入目录路径后遍历所有子目录和其中的文件。原本代码里有个注释掉的条件&& fileName[strlen(fileName)-4] == '.',保留的时候程序运行正常,但把这个条件去掉后,有些实际存在的文件会返回errno == 2,还有一些返回errno == 20。我想修改代码,不需要保留那个注释条件也能正确识别所有文件,请问该怎么改?
原代码
node* directories(char* fileName, node* result){ DIR *din = opendir(fileName); char currdirec[9999]; if(errno == 20) //&& fileName[strlen(fileName)-4] == '.') { node* list = createList(fileName); head = NULL; head = mergeLists(result, list); } else { struct dirent *dir; getcwd(currdirec, sizeof(currdirec)); char* temp = currdirec; strcat(currdirec, "/"); strcat(currdirec, fileName); chdir(currdirec); while((dir = readdir(din)) != NULL) { if(dir->d_type == DT_REG) { directories(dir->d_name, result); } else if(dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) { directories(dir->d_name, result); chdir(currdirec); } } chdir(temp); } return result; }
注:fileName是命令行传入的目录或文件。
问题分析
先明确两个错误码的含义:
errno == 2是ENOENT:表示请求的文件/目录不存在errno == 20是ENOTDIR:表示传入的路径不是目录
你的代码核心问题有这几个:
- 错误的
errno检查时机:opendir成功时不会修改errno,此时errno可能保留之前的错误值,直接判断errno ==20完全不可靠,必须先检查opendir的返回值是否为NULL,再去看errno。 - 工作目录切换导致的路径混乱:递归调用时切换了当前工作目录,后续处理文件时只传文件名,在嵌套目录场景下,这个文件名相对路径已经无效了,所以会出现
ENOENT。 - 全局变量
head的滥用:函数里直接操作全局head,会导致列表合并逻辑混乱,递归时的结果无法正确传递。 - 未关闭目录句柄:
opendir后没有调用closedir,会造成资源泄漏。
修复后的代码
我重写了核心逻辑,去掉了容易出错的目录切换,改用完整路径拼接的方式,同时修正了错误处理逻辑:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <errno.h> #include <unistd.h> // 假设你的node结构和辅助函数定义如下(根据实际情况调整) typedef struct node { char* path; struct node* next; } node; node* createList(char* path) { node* newNode = malloc(sizeof(node)); newNode->path = strdup(path); // 复制路径避免原指针失效 newNode->next = NULL; return newNode; } node* mergeLists(node* a, node* b) { if (a == NULL) return b; node* temp = a; while (temp->next != NULL) temp = temp->next; temp->next = b; return a; } node* directories(char* basePath, node* result) { // 先尝试打开目标路径 DIR *din = opendir(basePath); if (din == NULL) { // 打开失败,判断是否是普通文件 if (errno == ENOTDIR) { // 是普通文件,直接加入结果列表 return mergeLists(result, createList(basePath)); } else { // 其他错误(比如权限不足、路径不存在),可选打印错误信息 fprintf(stderr, "无法访问路径: %s - 错误: %s\n", basePath, strerror(errno)); return result; } } // 成功打开目录,遍历所有条目 struct dirent *dir; char fullPath[1024]; // 使用合理的缓冲区大小,避免溢出 while ((dir = readdir(din)) != NULL) { // 跳过当前目录和上级目录 if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { continue; } // 拼接完整路径,用snprintf避免缓冲区溢出 snprintf(fullPath, sizeof(fullPath), "%s/%s", basePath, dir->d_name); if (dir->d_type == DT_REG) { // 普通文件,直接添加到结果列表 result = mergeLists(result, createList(fullPath)); } else if (dir->d_type == DT_DIR) { // 子目录,递归遍历 result = directories(fullPath, result); } // 若需要处理符号链接、设备文件等类型,可在此添加对应逻辑 } closedir(din); // 关闭目录句柄,释放资源 return result; }
关键修改点说明
- 移除
chdir操作:改用拼接完整路径的方式处理所有文件/目录,彻底避免了工作目录切换带来的路径混乱问题,这是解决ENOENT错误的核心。 - 正确的错误处理:只有当
opendir返回NULL时才检查errno,确保错误判断的准确性。 - 去掉全局变量依赖:函数直接返回合并后的结果列表,逻辑更清晰,递归时结果传递更可靠。
- 安全的路径拼接:用
snprintf代替strcat,避免缓冲区溢出的风险。 - 添加
closedir:修复资源泄漏问题。
这样修改后,不管是传入目录还是单个文件,都能正确识别,不需要依赖那个注释掉的文件名后缀判断条件了。
内容的提问来源于stack exchange,提问作者Joe Cur
相关产品推荐
相关产品推荐

