readdir()返回文件名时,stat()错误'没有这样的文件或目录'

时间:2011-02-26 08:45:02

标签: c unix stat

我无法识别stat引发的错误。以下程序读取目录中的所有文件并打印文件名:

DIR *dp;
struct dirent *dirp;
struct stat sb;

if((dp = opendir(argv[1]))==NULL)
{
    perror("can't open dir");
}
while((dirp = readdir(dp))!=NULL)
{
    if (stat(dirp->d_name, &sb) == -1) {
        perror("stat");
    }   
    printf("File name:               %s \n",dirp->d_name);
}

示例输出:

/home/eipe
stat error: No such file or directory
File name:               copyofsample 
File name:               a.out 
File name:               . 
stat error: No such file or directory
File name:               udpclient.c 
File name:               .. 
stat error: No such file or directory
File name:               client.c 
stat error: No such file or directory
File name:               ftpclient.c 

以下是内容:

ls -l /home/eipe/c

-rwxr-xr-x 1 eipe egroup 7751 2011-02-24 15:18 a.out
-rw-r--r-- 1 eipe egroup  798 2011-02-24 13:50 client.c
-rw-r--r-- 1 eipe egroup   15 2011-02-24 15:34 copyofsample
-rw-r--r-- 1 eipe egroup 1795 2011-02-24 15:33 ftpclient.c
-rw-r--r-- 1 eipe egroup  929 2011-02-24 13:34 udpclient.c

2 个答案:

答案 0 :(得分:14)

dirp->d_name是目录中文件的名称:例如"udpclient.c"。因此,文件的全名为"/home/eipe/c/udpclient.c" - 但您当前的工作目录为/home/eipe,因此stat()正在尝试访问不存在的"/home/eipe/udpclient.c"

您可以使用argv[1]将工作目录更改为chdir(),也可以在致电argv[1]之前将stat()添加到每个文件名中。

答案 1 :(得分:3)

请注意,POSIX 2008引入了fstatat()和相关函数(系统调用),所有这些函数都以at后缀区分为熟悉的函数名称。这些函数占用一个(或renameat()的两个)打开文件描述符引用一个目录。这意味着在支持fstatat()的系统上编写此代码的另一种方法是:

DIR *dp;
struct dirent *dirp;
struct stat sb;
int dfd = open(argv[1], O_RDONLY);
if (dfd == -1)
{
    fprintf(stderr, "Failed to open directory %s for reading (%d: %s)\n",
            argv[1], errno, strerror(errno));
    return -1;
}

if ((dp = opendir(argv[1]))==NULL)
{
    perror("can't open dir");
    return -1;
}
while ((dirp = readdir(dp)) != NULL)
{
    if (fstatat(dfd, dirp->d_name, &sb, 9) == -1) {
        fprintf(stderr, "fstatat(\"%s\") failed (%d: %s)\n",
                dirp->d_name, errno, strerror(errno));
    }   
    printf("%-20s %s\n", "File name:", dirp->d_name);
}

使用fstatat()和相关功能,您可以在不使用chdir()的情况下使用相对路径名(这很危险;很难在不使用fchdir()的情况下返回到您开始的位置),或者串联名称,如主要接受的答案中所示。为了便于携带,可能仍然建议使用连接 - 但我可以使用下面的代码在Mac OS X(10.10.1)和Linux(Ubuntu 14.04)上测试它。

开发成一个完整的程序(test-fstatat.c):

#define _XOPEN_SOURCE 700
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s directory [...]\n", argv[0]);
        return -1;
    }

    for (int i = 1; i < argc; i++)
    {
        DIR *dp;
        struct dirent *dirp;
        struct stat sb;
        int dfd = open(argv[i], O_RDONLY);
        if (dfd == -1)
        {
            fprintf(stderr, "Failed to open directory %s for reading (%d: %s)\n",
                    argv[i], errno, strerror(errno));
            continue;
        }
        if (fstat(dfd, &sb) != 0 || !S_ISDIR(sb.st_mode))
        {
            errno = ENOTDIR;
            fprintf(stderr, "%s: %d %s\n", argv[i], errno, strerror(errno));
            continue;
        }

        if ((dp = opendir(argv[i]))==NULL)
        {
            perror("can't open dir");
            continue;
        }
        printf("%-20s %s\n", "Directory:", argv[i]);

        while ((dirp = readdir(dp)) != NULL)
        {
            if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
                fprintf(stderr, "fstatat(\"%s\") failed (%d: %s)\n",
                        dirp->d_name, errno, strerror(errno));
                continue;
            }   
            printf("%-20s %s\n", "File name:", dirp->d_name);
        }
        closedir(dp);
        close(dfd);
    }
    return 0;
}

示例运行:

$ test-fstatat ~/bin/JLSS-Dist/RCS ../src/sqltools/idsmon
Directory:           /Users/jleffler/bin/JLSS-Dist/RCS
File name:           .
File name:           ..
File name:           bomrelease.pl,v
File name:           chkbodlst.sh,v
File name:           chkmsdnmd.sh,v
File name:           chksumtool.pl,v
File name:           jdcrelease.sh,v
File name:           JLSS-Dist.mk,v
File name:           jlss.sh,v
File name:           mkbod.sh,v
File name:           mkmsd.sh,v
File name:           mknmd.sh,v
File name:           msd.create.sh,v
File name:           MSD.sh,v
File name:           prodverstamp.sh,v
File name:           publictimestamp.sh,v
File name:           redonmd.sh,v
Directory:           ../src/sqltools/idsmon
File name:           .
File name:           ..
File name:           acsetup.sh
File name:           dumpdblflt
File name:           dumpdblflt.c
File name:           idsmon
File name:           idsmon.o
File name:           idspacket
File name:           idspacket.c
File name:           idspacket.o
File name:           idstest
File name:           idstest.c
File name:           idstest.o
$