问题介绍

最近使用Linux C进行编程的时候,当递归读取目录的时候会发现stat函数一直再报错(No such file or directory),经过一晚上的修改bug,发现我的代码中的stat函数只能访问用户当前所在的路径下的文件(即’pwd‘命令所提示的目录)。

例如:此时我所在的路径为:/home/mrzhi/Desktop/Linux-网络编程/Unix_systems_programming/Chapter5_File_system

结果:可以发现该代码可以正确访问所在路径下的文件,但是该路径下的文件夹却无法递归访问。但是该代码中的opendir和reddir函数可以正确打开和读取该路径下的文件夹。但是却无法使用stat函数返回文件的属性。

图1. 结果描述

问题分析

这里我们先查看一下stat函数的定义

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
/* These  functions  return  information about a file, in the buffer pointed to by statbuf.
   No permissions are required on the file itself, **but—in the case of stat(), fstatat(),
   and lstat()—execute (search) permission is required on all of the directories in pathname that lead to the file.**
 * stat() and fstatat() retrieve information about the file pointed to by pathname;
   the differences for fstatat() are described below.
 */

可以发现其中有这样一句话:but——in the case of stat, fstatat(), and lstat() - execute (search) permission is required on all of the directories in pathname that lead to the file。
这句话的意思是路径名中指向该文件的所有目录都必须具有执行(搜索权限)。也就是我所在目录下的test必须具备执行或搜索权限。

错误原因:

只有文件为绝对路径的情况下,才可以获取文件的stat状态。而刚开始的代码中直接为./test/bbb.txt,此时pathname会直接访问根目录下的test/bbb.txt,但是我的根目录下没有这个文件,因此会报“No Such file or directory”的错误。要切记使用绝对路径。

代码示例

我的代码示例如下:

**// 这个一个使用opendir,readdir和closedir等对目录操作的函数
// 该程序显示的是目录中包含的文件名,其实这个目录的路径名被作为命令行参数传送
// note:ls命令会按照字母顺序对文件名进行排序,而readdir函数则按照起在目录文件中出现的顺序显示文件名
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
int printDir (char *path) {
    DIR *dr;
    int ret;
    struct dirent *direntp;
    struct stat *statbuf;    // struct stat 用来保存stat和lstat函数中返回的信息
    // note: 如果statbuf使用指针,则需要使用malloc函数为其分配内存空间,不如直接使用statbuf(不加指针)方便
    //printf("sizeof(struct stat) = %d\n", sizeof(struct stat));
    
    // 使用opendir函数打开所指定的目录
    dr = opendir(path);
    if(dr == NULL) {
        perror("Failed to open directory");
        return -1;
    }
    // 打印目录中的文件
    statbuf = (struct stat*)malloc(sizeof(struct stat));
    while ( (direntp = readdir(dr)) != NULL) {
        // fprintf(stderr, "打印文件: ");
        // 调用stat函数查看文件的状态
        fprintf(stderr, "%s", direntp->d_name);
        /* 在该函数中,stat只能访问用户所在的当前目录-->当前的原因我也不清楚*/
        ret = stat(direntp->d_name, statbuf);
        if (ret == -1) {
            //fprintf(stderr, "errno is %d\n", errno);
            perror(" : Failed to get file status");
            //return -1;
        } else {
            // 其中st_atim表示最后一次访问的时间,st_time表示最后一次数据修改的时间
            // st_ctime表示最后一次文件状态改变的时间
            // ctime返回的字符串会以换行符结束,因此可以不用\n
            printf(" : last accessed at %s", ctime(&(statbuf->st_atim)) );
            // 如果该文件是个目录,则递归打印目录中的文件
            if (*direntp->d_name != '.' && S_ISDIR(statbuf->st_mode)) {
                printf("***************************%s is directory!***********\n", direntp->d_name);
                printDir(direntp->d_name);
                printf("***************************%s ********************\n", direntp->d_name);
            }
        }
    }
    while (direntp == NULL && errno == EINTR);
    // note:使用完毕后一定要关闭目录
    closedir(dr);
}
int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s dir\n", argv[1]);
        return -1;
    }
    //printf("%s\n", argv[1]);
    int ret = printDir(argv[1]);
    return 0;
}**

修改后的代码:

只有文件为绝对路径的情况下,才可以获取文件的stat状态。因此这里对printDir函数中的while部分代码进行修改即可。

// 打印目录中的文件
    while ( (direntp = readdir(dr)) != NULL) {
        // fprintf(stderr, "打印文件: ");
        fprintf(stderr, "%s", direntp->d_name);

        char buf[1024] = {0};
        sprintf(buf, "%s/%s", path, direntp->d_name);

        // 调用stat函数查看文件的状态
        /* 在该函数中,stat只能访问用户所在的当前目录-->是路径的问题, 需要绝对路径才可以访问*/
        ret = stat(buf, &statbuf);
        if (ret == -1) {
            perror(" : Failed to get file status");
            //return -1;
        } else {
            // 其中st_atim表示最后一次访问的时间,st_time表示最后一次数据修改的时间
            // st_ctime表示最后一次文件状态改变的时间
            // ctime返回的字符串会以换行符结束,因此可以不用\n
            printf(" : last accessed at %s", ctime(&statbuf.st_atim) );

            // 如果该文件是个目录,则递归打印目录中的文件
            if (*direntp->d_name != '.' && S_ISDIR(statbuf.st_mode)) {
                
                // 当递归打印目录的时候,需要指定绝对路径,此时为 ./test
                //sprintf(buf + strlen(buf), "/%s", direntp->d_name);

                printf("***************************%s is directory!***********\n", buf);

                printDir(buf);

                printf("***************************%s ********************\n", buf);
            }
        }

    }

note:以上问题是我在编程的时候遇到的,解决方法为经过百度和差异相关文献(由于本人水平有限,可能解答有错误)。如有错误之处,请多多指正并谅解。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐