Переберите каталог и напечатайте некоторую информацию - PullRequest
0 голосов
/ 07 января 2019

Я хочу написать программу на C, которая принимает в качестве аргумента путь к папке и показывает некоторую информацию о файлах, которые она содержит.

Пока я написал это:

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char** argv){
    char* dir_path = argv[1];
    char* dir_path_bar = strcat(dir_path, "/");
    DIR* dir = opendir(dir_path);

    for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)){
        printf("Next entry is %s\n", entry->d_name);
        char* entry_path = strcat(dir_path_bar, entry->d_name);
        printf("%s\n", entry_path);
        struct stat buf;
        stat(entry_path, &buf);
        printf("Its inode number is %s\n", entry->d_ino);
        printf("Its inode number is %s\n", buf.st_ino);
        printf("Its uid is %s\n", buf.st_uid);
        printf("Its size is %s bytes\n", buf.st_size);
    };
    closedir(dir);
}

Что компилируется, но вызов stat дает мне SEGFAULT. Что происходит?

Ответы [ 3 ]

0 голосов
/ 07 января 2019

Как уже упоминалось, вы не можете добавить к argv[1]. Вы не можете продолжать добавлять к нему внутри цикла. И вы не можете использовать %s для вывода чисел.

Вот ваш код с аннотированными и исправленными ошибками [используя #if 0 для отображения старого кода]:

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
    char *dir_path = argv[1];

// NOTE/BUG: argv[1] has a fixed size you can't append to it
#if 0
    char *dir_path_bar = strcat(dir_path, "/");
#else
    char dir_path_bar[PATH_MAX];
    strcpy(dir_path_bar,dir_path);
    strcat(dir_path_bar,"/");
#endif

    DIR *dir = opendir(dir_path);
#if 1
    if (dir == NULL) {
        perror(dir_path);
        exit(1);
    }
#endif

    for (struct dirent *entry = readdir(dir); entry != NULL;
        entry = readdir(dir)) {
        printf("Next entry is %s\n", entry->d_name);

// NOTE/BUG: because you don't reset dir_path_bar, this just keeps appending
// to it
#if 0
        char *entry_path = strcat(dir_path_bar, entry->d_name);
#else
        char entry_path[PATH_MAX];
        strcpy(entry_path,dir_path_bar);
        strcat(entry_path,entry->d_name);
#endif

        printf("\n");
        printf("%s\n", entry_path);
        struct stat buf;

        stat(entry_path, &buf);

// NOTE/BUG: these need one or more of: %d/%ld/%lld (vs %s)
#if 0
        printf("Its inode number is %s\n", entry->d_ino);
        printf("Its inode number is %s\n", buf.st_ino);
        printf("Its uid is %s\n", buf.st_uid);
        printf("Its size is %s bytes\n", buf.st_size);
#else
        printf("Its inode number is %ld\n", entry->d_ino);
        printf("Its inode number is %ld\n", buf.st_ino);
        printf("Its uid is %d\n", buf.st_uid);
        printf("Its size is %ld bytes\n", buf.st_size);
#endif
    };

    closedir(dir);

    return 0;
}
0 голосов
/ 08 января 2019

Не показано в двух других предыдущих ответах - хорошее предотвращение чрезмерного копирования.

При формировании entry_path необходимо перезаписать только саму запись, а не всю строку. Это становится полезным с длинной предварительно фиксированной строкой каталога.

    dir_path_len = strlen(dir_path);
    if (dir_path_len >= PATH_MAX - 1) { return EXIT_FAILURE; } // too long

    char entry_path[PATH_MAX];
    strcpy(entry_path, dir_path);
    strcpy(entry_path + dir_path_len++, "/"); // Can use strcpy() here

    DIR *dir = opendir(dir_path);
    ...
    for (struct dirent *entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
        printf("Next entry is %s\n", entry->d_name);

        entry_len = strlen(entry->d_name);
        if (dir_path_len + entry_len >= PATH_MAX) { 
            continue;
            // or 
            return EXIT_FAILURE; // too long
        }
        strcpy(path + dir_path_len, entry->d_name);  // strcpy(), not strcat()

        printf("\n%s\n", entry_path);

        struct stat buf;
        if (stat(entry_path, &buf) ...
        ...
0 голосов
/ 07 января 2019

Две проблемы:

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

  • Также печатает целочисленные значения, используя %s, который также не определен. % s ожидает аргумент char *, но вы хотите вывести целочисленные значения.

Вместо этого вы можете использовать временный буфер и передать его в stat(2):

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    if (argc != 2) {
        printf("Usage: %s dir\n", argv[0]);
        exit(1);
    }

    char* dir_path = argv[1];
    DIR* dir = opendir(dir_path);

    if (!dir) {
        perror("opendir");
        exit(1);
    }

    for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
        char entry_path[PATH_MAX] = {0};
        int rc = snprintf(entry_path, sizeof entry_path, "%s/%s", dir_path, entry->d_name);

        if ( rc < 0 || rc >= sizeof entry_path) {
            fprintf(stderr, "Path truncated for '%s'\n", entry->d_name);
            continue;
        }
        printf("Next entry is: %s\n", entry_path);
        struct stat buf;
        if (stat(entry_path, &buf) == 0) {
            printf("Its inode number is %ju\n", (uintmax_t)entry->d_ino);
            printf("Its inode number is %ju\n", (uintmax_t)buf.st_ino);
            printf("Its uid is %jd\n", (intmax_t)buf.st_uid);
            printf("Its size is %jd bytes\n", (intmax_t)buf.st_size);
        } else {
            perror("stat");
        }
    }

    closedir(dir);
}

Я также добавил некоторые проверки ошибок.

...