You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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:表示传入的路径不是目录

你的代码核心问题有这几个:

  1. 错误的errno检查时机:opendir成功时不会修改errno,此时errno可能保留之前的错误值,直接判断errno ==20完全不可靠,必须先检查opendir的返回值是否为NULL,再去看errno。
  2. 工作目录切换导致的路径混乱:递归调用时切换了当前工作目录,后续处理文件时只传文件名,在嵌套目录场景下,这个文件名相对路径已经无效了,所以会出现ENOENT。
  3. 全局变量head的滥用:函数里直接操作全局head,会导致列表合并逻辑混乱,递归时的结果无法正确传递。
  4. 未关闭目录句柄: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;
}

关键修改点说明

  1. 移除chdir操作:改用拼接完整路径的方式处理所有文件/目录,彻底避免了工作目录切换带来的路径混乱问题,这是解决ENOENT错误的核心。
  2. 正确的错误处理:只有当opendir返回NULL时才检查errno,确保错误判断的准确性。
  3. 去掉全局变量依赖:函数直接返回合并后的结果列表,逻辑更清晰,递归时结果传递更可靠。
  4. 安全的路径拼接:用snprintf代替strcat,避免缓冲区溢出的风险。
  5. 添加closedir:修复资源泄漏问题。

这样修改后,不管是传入目录还是单个文件,都能正确识别,不需要依赖那个注释掉的文件名后缀判断条件了。

内容的提问来源于stack exchange,提问作者Joe Cur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:01:04