После открытия каталога его элементы читаются последовательно до конца каталога.
Системный вызов
readdir
возвращает имя следующего файла в каталоге. Каталоги не упорядочены каким-либо образом, поэтому не стоит предполагать, что оглавление каталога отсортировано. Если необходим упорядоченный список файлов, сортировку придется выполнять самостоятельно. Функция
readdir
определяется, как показано ниже.
#include <dirent.h>
struct dirent * readdir (DIR * dir);
Вызывающему коду возвращается указатель на структуру
struct dirent
. Несмотря на то что
struct dirent
содержит несколько элементов, единственным переносимым элементом является
d_name
, содержащий имя файла элемента каталога. Остальные элементы
struct dirent
зависят от системы. Однако интересным является элемент
d_ino
, содержащий inode-номер файла.
Самой сложной частью этого процесса является определение ошибки. К сожалению,
readdir
возвращает
NULL
, и когда происходит ошибка, и когда в каталоге больше нет элементов. Чтобы различать эти две ситуации, необходимо проверять
errno
. Эта задача усложняется тем, что
readdir
не меняет
errno
, пока не произойдет ошибка. Это означает, что для корректной проверки ошибок
errno
необходимо установить перед вызовом
readdir
в заранее известное значение (обычно 0). Ниже показана простая программа, записывающая имена файлов текущего каталога в stdout.
1: /* dircontents.с */
2:
3: #include <errno.h>
4: #include <dirent.h>
5: #include <stdio.h>
6:
7: int main(void) {
8: DIR * dir;
9: struct dirent * ent;
10:
11: /* "." - текущий каталог */
12: if (!(dir = opendir("."))) {
13: perror("opendir");
14: return 1;
15: }
16:
17: /* установить errno в 0, чтобы можно было выяснить, когда readdir даст сбой*/
18: errno = 0;
19: while ((ent = readdir(dir))) {
20: puts (ent->d_name);
21: /* сбросить errno, поскольку puts может модифицировать ее */
22: errno = 0;
23: }
24:
25: if (errno) {
26: perror("readdir");
27: return 1;
28: }
29:
30: closedir(dir);
31:
32: return 0;
33: }
14.4.1. Прохождение по каталогу
Если требуется перечитать содержимое каталога, уже открытого
opendir
, с помощью
rewinddir
структура
DIR
сбрасывается, чтобы следующий вызов
readdir
мог вернуть первый файл в каталоге.
#include <dirent.h>
int rewinddir(DIR * dir);
14.5. Универсализация файловых имен
Большинство
пользователей Linux принимают как должное то, что запуск
ls *.с
не сообщает сведения о файле в текущем каталоге, именем которого является
*.с
. Вместо этого они ожидают увидеть список всех файлов в текущем каталоге, имена которых заканчиваются на
.с
. Это расширение имени файла от
*.с
до
ladsh.с dircontents.с
(например) обычно обрабатывается оболочкой, которая универсализирует все параметры для программ, выполняющихся под ее управлением. Программы, помогающие пользователям манипулировать файлами, тоже часто нуждаются в универсализации файловых имен. Существуют два распространенных способа универсализации имен файлов внутри программ.
14.5.1. Использование подпроцесса
Самый старый метод универсализации предусматривает запуск оболочки в качестве дочернего процесса и указание ей универсализировать файловые имена. Стандартная функция
popen
(см. главу 10) упрощает этот метод — просто запустите команду
ls *.с
с помощью
popen
и прочитайте результат. Этот подход может показаться несколько упрощенным, но все же он обеспечивает переносимое решение проблемы универсализации (вот почему приложения вроде Perl используют его).
Ниже приведена программа, которая универсализирует все аргументы и отображает все совпадения.
1: /* popenglob.c */
2:
3: #include <stdio.h>
4: #include <string.h>
5: #include <sys/wait.h>
6: #include <unistd.h>
7:
8: int main(int argc, const char ** argv)
9: char buf[1024];
10: FILE * ls;
11: int result;
12: int i;
13:
14: strcpy(buf, "ls");
15:
16: for (i = 1; i < argc; i++) {
17: strcat(buf, argv[i]);
18: strcat(buf, " ");
19: }
20:
21: ls = popen(buf, "r");
22: if (!ls) {
23: perror("popen");
24: return 1;
25: }
26:
27: while (fgets(buf, sizeof(buf), ls))
28: printf("%s", buf);
29:
30: result = pclose(ls);
31:
32: if (!WIFEXITED(result)) return 1;
33:
34: return 0;
35: }
14.5.2. Внутренняя универсализация
Если необходимо универсализировать несколько файловых имен, запуск нескольких подоболочек с помощью
popen
будет неэффективным. Функция
glob
позволяет универсализировать имена файлов без запуска каких-либо подпроцессов, однако за счет увеличения сложности и снижения переносимости. Несмотря на то что вызов
glob
описан в стандарте POSIX.2, многие варианты Unix до сих пор его не поддерживают.