На одном из почтовых серверов у меня есть директория в которую складываются пришедшие письма. За некоторое время количество файлов в этой директории начинает превышать несколько тысяч и стандартной командой удаления 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), а жаль. Судя по описанию уже по этому полю можно было бы определить тип записи: файл, директория или символьная ссылка. Ну ничего страшного.