Книга: Основы программирования в Linux
closedir
closedir
Функция closedir
закрывает поток каталога и освобождает ресурсы, выделенные ему. Она возвращает 0 в случае успеха и -1 при наличии ошибки.
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
В приведенной далее программе printdir.c (упражнение 3.4) вы соберете вместе множество функций обработки файлов для создания простого перечня содержимого каталога. Каждый файл представлен отдельной строкой. У каждого подкаталога есть имя, за которым следует слэш, и файлы, содержащиеся в подкаталоге, выводятся с отступом шириной в четыре пробела.
Программа превращает каталог в подкаталоги, чтобы у найденных файлов были пригодные для использования имена, т.е. они могут передаваться непосредственно в функцию opendir. Программа будет давать сбои при просмотре структур с большим числом уровней вложенности, поскольку есть ограничение на разрешенное число открытых потоков каталогов.
Мы могли бы сделать программу более универсальной, принимая в качестве аргумента командной строки начальную точку просмотра.
Для того чтобы познакомиться с методами повышения универсальности программ, посмотрите исходный код таких утилит Linux, как ls
и find
.
Упражнение 3.4. Программа просмотра каталога
1. Начните с соответствующих заголовочных файлов и функции printdir
, которая выводит содержимое текущего каталога. Она будет рекурсивно вызываться для вывода подкаталогов, применяя параметр depth
для задания отступа.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
void printdir(char *dir, int depth) {
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if ((dp = opendir(dir)) == NULL) {
fprintf(stderr, "cannot open directory: %sn", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
/* Находит каталог, но игнорирует . и .. */
if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
continue;
printf("%*s%s/n", depth, "", entry->d_name);
/* Рекурсивный вызов с новый отступом */
printdir(entry->d_name, depth+4);
} else printf("%*s%sn", depth, " ", entry->d_name);
}
chdir("..");
closedir(dp);
}
2. Теперь переходите к функции main
.
int main() {
/* Обзор каталога /home */
printf("Directory scan of /home:n");
printdir("/home", 0);
printf("done.n");
exit(0);
}
Программа просматривает исходные каталоги и формирует вывод, похожий на приведенный далее (отредактированный для краткости). Для того чтобы заглянуть в каталоги других пользователей, вам могут понадобиться права доступа суперпользователя.
$ ./printdir
Directory scan of /home:
neil/
.Xdefaults
.Xmodmap
.Xresources
.bash_history
.bashrc
.kde/
share/
apps/
konqueror/
dirtree/
public_html.desktop
toolbar/
bookmarks.xml
konq_history
kdisplay/
color-schemes/
BLP4e/
Gnu_Public_License
chapter04/
argopt.с
args.с
chapter03/
file.out
mmap.с
printdir
done.
Как это работает
Большинство операций сосредоточено в функции printdir
. После некоторой начальной проверки ошибок с помощью функции opendir
, проверяющей наличие каталога, printdir
выполняет вызов функции chdir
для заданного каталога. До тех пор пока элементы, возвращаемые функцией readdir
, не нулевые, программа проверяет, не является ли очередной элемент каталогом. Если нет, она печатает элемент-файл с отступом, равным depth
.
Если элемент — каталог, вы встречаетесь с рекурсией. После игнорирования элементов .
и ..
(текущего и родительского каталогов) функция printdir
вызывает саму себя и повторяет весь процесс снова. Как она выбирается из этих повторений? Как только цикл while
заканчивается, вызов chdir("..")
возвращает программу вверх по дереву каталогов, и предыдущий перечень можно продолжать. Вызов closedir(dp)
гарантирует, что количество открытых потоков каталогов не больше того, которое должно быть.
Для того чтобы составить представление об окружении в системе Linux, обсуждаемом в главе 4, познакомьтесь с одним из способов, повышающих универсальность программы. Рассматриваемая программа ограничена, потому что привязана каталогу /home. Следующие изменения в функции main
могли бы превратить эту программу в полезный обозреватель каталогов:
int main(int argc, char* argv[]) {
char *topdir = ".";
if (argc >= 2) topdir = argv[1];
printf("Directory scan of %sn", topdir);
printdir(topdir, 0);
printf("done.n");
exit(0);
}
Три строки изменены и пять добавлено, но это уже универсальная утилита с необязательным параметром, содержащим имя каталога, по умолчанию равным текущему каталогу. Вы можете выполнять ее с помощью следующей командной строки:
$ ./printdir2 /usr/local | more
Вывод будет разбит на страницы, и пользователь сможет листать их. Таким образом, у него появится маленький удобный универсальный обозреватель дерева каталогов. Приложив минимум усилий, вы могли бы добавить статистический показатель использования пробелов, предельную глубину отображения и т.д.