LINUX.ORG.RU

Рекурсивно получить содержимое директорий (очень нубский вопрос)

 , ,


0

1

Сейчас сижу, втыкаю в код:

     #include <stdio.h>
     #include <sys/types.h>
     #include <dirent.h>
     
     int
     main (void)
     {
       DIR *dp;
       struct dirent *ep;
     
       dp = opendir ("./");
       if (dp != NULL)
         {
           while (ep = readdir (dp))
             puts (ep->d_name);
           (void) closedir (dp);
         }
       else
         perror ("Couldn't open the directory");
     
       return 0;
     }
Можете по хардкору пояснить, как теперь сделать рекурсивную читалку директорий? Думаю, что утром и без подсказок сделаю, но сейчас в упор подвоха не вижу.

★★★★★

Последнее исправление: CYB3R (всего исправлений: 1)
Ответ на: комментарий от beastie

Странный какой-то пример, названия директорий выводит дважды.
Сделал небольшой фикс: будет печатать названия директорий один раз, добавляя в конце «/», чтобы отличать от файлов, а ещё будет скрывать файлы, начинающиеся с точки.

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
/* "readdir" etc. are defined here. */
#include <dirent.h>
/* limits.h defines "PATH_MAX". */
#include <limits.h>

/* List the files in "dir_name". */

static void
list_dir (const char * dir_name)
{
    DIR * d;

    /* Open the directory specified by "dir_name". */

    d = opendir (dir_name);

    /* Check it was opened. */
    if (! d) {
        fprintf (stderr, "Cannot open directory '%s': %s\n",
                 dir_name, strerror (errno));
        exit (EXIT_FAILURE); /* здесь exit вообще уместен? 0_о */
/* если вдруг ошибка, где-то в середине, программа завершится, не закончив досчитывать директории */
    }
    while (1) {
        struct dirent * entry;
        const char * d_name;

        /* "Readdir" gets subsequent entries from "d". */
        entry = readdir (d);
        if (! entry) {
            /* There are no more entries in this directory, so break
               out of the while loop. */
            break;
        }
        d_name = entry->d_name;
        /* Print the name of the file and directory. */
        if (strncmp(d_name, ".", strlen("."))) /* мой фикс #1: не показываем файлы, начинающиеся с "." */
/* не уверен, будет ли константа одинаково себя вести на всех системах (битность, все дела), потому использую strlen */
            printf ("\n%s/%s", dir_name, d_name);

        /* See if "entry" is a subdirectory of "d". */

        if (entry->d_type & DT_DIR) {

            /* Check that the directory is not "d" or d's parent. */
            
            if (strcmp (d_name, "..") != 0 &&
                strcmp (d_name, ".") != 0) {
                int path_length;
                char path[PATH_MAX];
 
                path_length = snprintf (path, PATH_MAX,
                                        "%s/%s", dir_name, d_name);
                printf ("/"); /* мой фикс #2: добавим слеш к именам директорий */
                if (path_length >= PATH_MAX) {
                    fprintf (stderr, "Path length has got too long.\n");
                    exit (EXIT_FAILURE); /* здесь exit вообще уместен? 0_о */
/* если вдруг ошибка, где-то в середине, программа завершится, не закончив досчитывать директории */
                }
                /* Recursively call "list_dir" with the new path. */
                list_dir (path);
            }
        }
    }
    /* After going through all the entries, close the directory. */
    if (closedir (d)) {
        fprintf (stderr, "Could not close '%s': %s\n",
                 dir_name, strerror (errno));
        exit (EXIT_FAILURE); /* здесь exit вообще уместен? 0_о */
/* если вдруг ошибка, где-то в середине, программа завершится, не закончив досчитывать директории */
    }
}

int main ()
{
    list_dir ("/dev");
    return 0;
}
Нормально написал? И про использование exit везде: это нормально?

CYB3R ★★★★★
() автор топика
Последнее исправление: CYB3R (всего исправлений: 1)
Ответ на: комментарий от CYB3R

Для данной программы exit() везде это нормально, она всё равно больше ничего не делает. Хотя тот же ″ls″ ругается ″Permission denied″ на каталоги, на которые пользователю не хватает прав, но не завершает работу. Так что решать вам.

И ещё у вас не проверяется errno после readdir(), не уверен, но вроде как в случае сетевых файловых систем там легко возникает ошибка при сбоях сети, а ваша программа это просто проигнорирует и решит, что в каталоге больше нет файлов.

Это:

strncmp(d_name, ".", strlen("."))
по мне, совсе лишнее, делайте просто
 d_name[0] == '.' 
Во всяком случае в ядре пишут так. И, если не печатаете файлы, начинающиеся с точки, то не печайте и ″/″, если с точки начинается каталог.

И я не вижу, чтобы программа последним символом печатала ″\n″.

P.S. Зачем вы отметели тему решённой, если задали ещё вопросы?

mky ★★★★★
()
Ответ на: комментарий от CYB3R

нет.

отдели кости(рекурсивный обход и перебор entry) от мяса (отсичение ненужных тебе entry) т.е разнеси по функциям - э так перегруженноя «матаном» портянка

qulinxao ★★☆
()
Ответ на: комментарий от CYB3R

Фу таким быть.

Это ущербный гнутый стайл определения функций - говно, и юзабельно только тогда, когда у тебя экран 20символов в ширину, в остальных случая смотрится и места занимает как говно, поэтому не нужно.

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

static void for_dir(char * path, char * d_name) {//пиши чё надо.

  if(*d_name == '.') return;//или что-то похожее.

  char new_path[PATH_MAX];//Так, конечно, пишут только говнори
  snprintf(new_path, PATH_MAX, "%s/%s", path, d_name);//Тут что-то проверять нахрен не упало, ибо пациент несможет создать путь длиннее нуникак(ладно 0.01% сможет, но мы кладём на них).
  *(new_path+PATH_MAX-1) = 0;//можешь так сделать для надёжности, на тот шанс, который один на милион, чтобы оно отвалилось в опендире.
  
  list_dir(new_path);
}


static void list_dir(char * dir_name) { //так не пишут только идиоты.
    
    DIR * d = opendir(dir_name);//так не пишут только идиоты.
    if (!/*пробел тут ставят только идиоты*/d) {
        fprintf (stderr, "Cannot open directory '%s': %s\n", dir_name, strerror (errno));
        exit (EXIT_FAILURE);
    }//так пишут тоже только идиоты, ибо вынеси это куда-то в функцию.
    
    //так, как тут был написан цикл - так пишут только идиоты.
    
    struct dirent * entry;//Написал байду.
    
    while(entry = readdir(d)) {//написал нормальный цикл.
      if(!(entry->d_type ^ DT_DIR))//пиши чё тебе надо - мне лень.
	for_dir(dir_name, entry->d_name);
      else//можно выпилить
	fprintf(stdout, "\n%s/%s", dir_name, entry->d_name);
    }
    
    if(closedir(d)) {
        fprintf (stderr, "Could not close '%s': %s\n", dir_name, strerror (errno));
        exit (EXIT_FAILURE); /* здесь exit вообще уместен? 0_о */
    }
}

Вобщем я за 5секунд превратил твою портянку в 5строчек, а если это ещё и переписать нормально, то будет вообще отлично.

superhackkiller1997
()
Ответ на: Фу таким быть. от superhackkiller1997

посан. я почти так и пишу

anonymous
()
Ответ на: Фу таким быть. от superhackkiller1997

Да это и не моя портянка.
А про стиль кода... Я тоже так пишу, это компактнее, но если дан код с каким-то оформлением, то лучше его придерживаться.

CYB3R ★★★★★
() автор топика
4 марта 2014 г.
Ответ на: комментарий от CYB3R

если речь идет об подобном оформлении функции

int
func_name(void)

то делает ся это исключительно для последующего удобства работы с исходниками

в больших проектах очень просто найти файл в котором лежит функция, вот таким образом.

find . -type f | xargs grep ^func_name

grep -nHe ^func_name -r .

это единственный смысл «гнутого» оформления кода.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.