На одном из почтовых серверов у меня есть директория в которую складываются пришедшие письма. За некоторое время количество файлов в этой директории начинает превышать несколько тысяч и стандартной командой удаления rm *.eml удалить ничего не получается:
root@mailserver:/mail/queue# rm *.eml bash: /bin/rm: Argument list too long
Связано это с тем, что Bash из маски *.eml делает длинную строку из списка файлов с расширением eml, поэтому количество аргументов для команды rm превышает допустимое количество. Естественно удалить такое количество файлов можно с использованием других команд, но интерес не в этом, а в том чтобы разобраться с функциями чтения директорий.
Итак, приступим. Для работы с директориями необходимо подключить файлы:
#include <sys/types.h> #include <dirent.h>
Директория сама по себе представляет файл состоящий из специальных записей dirent, которые содержат данные о файлах в директории:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
Данная структура содержит имя файла d_name, порядковый номер файла d_ino в файловой системе и несколько других. Разберемся.
Для работы с директориями необходимо определить переменную типа DIR (по смыслу она похожа на тип FILE). Для открытия/закрытия директорий существует две функции:
DIR *opendir(const char *name); int closedir(DIR *dirp);
Функция opendir() открывает директорию для чтения с именем name и возвращает указатель на directory stream (иногда сложно перевести со смыслом, директорный поток или поток директории ?), при ошибке возвращает NULL и соответствующим образом устанавливает код ошибки errno. Функция closedir() без комментариев.
Чтение из этого файла-директории осуществляется функциями со схожими названиями:
struct dirent *readdir(DIR *dirp); int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
Первая функция readdir() возвращает следующую структуру dirent считанную из файла-директории. При достижении конца списка файлов в директории или возникновении ошибки возвращает NULL. Вторая функция должна использоваться для чтения содержимого директорий при разработке программ работающих в мультипоточном режиме.
Давайте попробуем прочитать какую-нибудь директорию:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main() {
DIR *dir;
struct dirent *entry;
dir = opendir("/");
if (!dir) {
perror("diropen");
exit(1);
};
while ( (entry = readdir(dir)) != NULL) {
printf("%d - %s [%d] %d\n",
entry->d_ino, entry->d_name, entry->d_type, entry->d_reclen);
};
closedir(dir);
return 0;
};
Компилируем и запускаем:
root@home:/root/dirent# gcc dirread.c root@home:/root/dirent# ./a.out 2 - . [0] 16 1 - .. [0] 16 114 - bin [0] 16 19 - dev [0] 16 109 - etc [0] 16 89 - lib [0] 16 117 - mnt [0] 16 127 - opt [0] 16 144 - srv [0] 16 112 - tmp [0] 16 108 - sys [0] 16 4 - var [0] 16 37 - usr [0] 16 113 - boot [0] 16 116 - home [0] 16 145 - proc [0] 16 115 - sbin [0] 16 149 - root [0] 16 691340 - readme.txt [0] 32 128 - media [0] 20 575756 - netio [0] 20
Из всего этого думаю полезностей извлечь можно не много. Думаю кроме имени файла d_name ничего особо полезного нет, но собственно больше ничего и не надо. Как и было написано в man 3 readdir поле d_type многие файловые системы не устанавливают (в примере используется reiserfs), а жаль. Судя по описанию уже по этому полю можно было бы определить тип записи: файл, директория или символьная ссылка. Ну ничего страшного.