stat () ошибка «Нет такого файла или каталога», когда readdir () возвращает имя файла - PullRequest
8 голосов
/ 26 февраля 2011

Я не могу определить ошибку, выданную 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 ]

14 голосов
/ 26 февраля 2011

dirp->d_name - это имя файла в каталоге : например, "udpclient.c". Полное имя файла, таким образом, "/home/eipe/c/udpclient.c", но ваш текущий рабочий каталог - /home/eipe, поэтому stat() пытается получить доступ к "/home/eipe/udpclient.c", которого не существует.

Вы можете изменить рабочий каталог на argv[1], используя chdir(), или вы можете добавить argv[1] к каждому имени файла перед вызовом stat().

3 голосов
/ 24 декабря 2014

Обратите внимание, что в 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
$
...