Удаление каталога — это практически то же, что и удаление файла; меняется разве что имя системного вызова.
#include <unistd.h>
int rmdir(char * pathname);
Для успешного выполнения
rmdir
каталог должен быть пустым (он не должен содержать ничего, кроме вездесущих
.
и
..
); в противном случае возвращается
ENOTEMPTY
.
14.4. Чтение содержимого каталога
Обычно программам требуется получать список файлов,
содержащихся в каталоге. Linux предоставляет ряд функций, позволяющих обрабатывать каталог как абстрактный объект, что дает возможность избежать зависимости программ от точного формата каталогов, реализуемого файловой системой. Открытие и закрытие каталогов осуществляется очень просто.
#include <dirent.h>
DIR * opendir(const char * pathname);
int closedir(DIR * dir);
Системный вызов
opendir
возвращает указатель на тип данных
DIR
, который является абстрактным (как и структура
stdio
по имени
FILE
) и которым не следует манипулировать вне библиотеки С. Поскольку каталоги можно открывать только для чтения, нет необходимости определять, в каком режиме открывается каталог,
opendir
срабатывает только в случае существования каталога — этот вызов нельзя использовать для создания новых каталогов (для этого служит
mkdir
). Закрытие каталога может не сработать только в случае некорректного значения аргумента
dir
.
После открытия каталога его элементы читаются последовательно до конца каталога.
Системный вызов
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 используют его).
Ниже приведена программа, которая универсализирует все аргументы и отображает все совпадения.