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

Блин, больше не буду велосипедить для лора. Пока напишешь, уже кто-то запостит рабочий вариант.

Кстати у меня DT_DIR на райзере не работает (и об этом в мане пишут), так что оставлю свои 5ть строк, может будет полезно.

int is_dir(struct dirent *e)
{
  struct stat st;
  memset(&st, 0, sizeof(st));
  lstat(e->d_name, &st);
  return S_ISDIR(st.st_mode);
}

nanoolinux ★★★★
()

Спасибо за ответы, написал то, что хотел.
Плюсик в карму вам.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от 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

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

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

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

int
func_name(void)

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

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

find . -type f | xargs grep ^func_name

grep -nHe ^func_name -r .

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

anonymous
()
Ответ на: комментарий от deterok

Если не сложно, обьясните почему виспользуют & для сравнения поля d_type? Чем == не устроило?

потому что

/usr/include/dirent.h

/* File types for `d_type'.  */
enum
  {
    DT_UNKNOWN = 0,
# define DT_UNKNOWN·DT_UNKNOWN
    DT_FIFO = 1,
# define DT_FIFO·   DT_FIFO
    DT_CHR = 2,
# define DT_CHR··   DT_CHR
    DT_DIR = 4,
# define DT_DIR··   DT_DIR
    DT_BLK = 6,
# define DT_BLK··   DT_BLK
    DT_REG = 8,
# define DT_REG··   DT_REG
    DT_LNK = 10,
# define DT_LNK··   DT_LNK
    DT_SOCK = 12,
# define DT_SOCK·   DT_SOCK
    DT_WHT = 14
# define DT_WHT··   DT_WHT
  };

т.е. это не число, а набор бит (bit set). И сравнивать их надо логической побитной AND.

emulek
()
Ответ на: комментарий от emulek

На мой взгляд древняя методика, скорость конечно должна возрасти, но сейчас уже это не сильно важно...

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

На мой взгляд древняя методика, скорость конечно должна возрасти, но сейчас уже это не сильно важно...

ну ты не забывай, что эта хрень ещё и во всяких говнороутерах работает и в прочих кофемолках. А не только в твоём локалхосте с LA==0.0. Ну и потом, зачем ломать то, что уже работает? В сишечке же нет битсетов, кроме вот таких вот.

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