Два способа прочесть содержимое директории
Задача перечисления всех элементов директории возникает довольно часто. Стандартная библиотека Linux предоставляет два способа перечисления содержимого директорий: первый способ – с помощью функции scandir() и функций обратного вызова, второй – с использованием набора функций opendir(), readdir(), closedir(). Рассмотрим два варианта программы printdir, распечатывающей содержимое директории, переданной ей в качестве аргумента.
#include <stdio.h> #include <dirent.h> int sel(struct dirent * d) { return 1; // всегда подтверждаем } int main(int argc, char ** argv) { int i, n; struct dirent ** entry; if (argc != 2) { printf("Использование: %s <директория>\n", argv[0]); return 0; } n = scandir(argv[1], &entry, sel, alphasort); if (n < 0) { printf("Ошибка чтения директории\n"); return 1; } for (i = 0; i < n; i++) printf("%s inode=%i\n", entry[i]->d_name, entry[i]->d_ino); return 0; }
Функция scandir() создает список элементов указанной директории. Ей необходимо передать указатель на функцию обратного вызова, которая, получая данные об очередном элементе, принимает решение, включать этот элемент в результирующий список. В нашем примере это функция sel(). Если при очередном вызове функция sel() вернет значение 0, соответствующий элемент директории не будет включен в конечный список. Последний параметр scandir - функция сортировки элементов директории. Мы используем функцию alphasort(), сортирующую элементы в лексикографическом порядке.
Данные об элементах директории передаются в структурах dirent. Можно было бы ожидать, что структуры типа dirent содержат много полезной информации об элементах директории, но это не так. Кроме имени файла dirent содержит номер inode для этого элемента (простым программам обычно не зачем знать номера inode, но, чтобы наш пример как-то отличался от стандартного, мы включаем эту информацию). У структуры dirent есть еще поле d_type типа char *, но оно, как правило, содержит null.
Функция scandir() позволяет нам получить полный отсортированный список элементов директории за один вызов. У нас есть возможность использовать низкоуровневые средства, которые могут оказаться быстрее в том случае, если сортировка файлов нам не нужна. Рассмотрим второй вариант программы:
#include <stdio.h> #include <dirent.h> int main(int argc, char ** argv) { DIR * d; struct dirent * entry; if (argc != 2) { printf("Использование: %s <директория>\n", argv[0]); return 0; } d = opendir(argv[1]); if (d == NULL) { printf("Ошибка чтения директории\n"); return 1; } while (entry = readdir(d)) printf("%s inode=%i\n", entry->d_name, entry->d_ino); closedir(d); return 0; }
Этот вариант программы использует функции opendir(), readdir() и closedir(), которые работают с директорией как с файлом. Функция readdir() возвращает значение TRUE до тех пор, пока не будут прочитаны все элементы директории.