Вспомним, что директории — это древовидные структуры. Сама директория играет роль ствола, а содержащиеся в ней поддиректории — ветки. Обычные файлы — что-то вроде листьев. Дерево является рекурсивной структурой, и логично для обработки дерева применить рекурсивный алгоритм.
В основе такого алгоритма будет лежать процедура, принимающая имя как параметр
и обрабатывающая это имя. Процедура выведет его, если имя принадлежит
существующему обычному файлу. Если имя относится к директории, то для такого
случая предусмотрен особый план действий. В цикле по очереди обрабатываются все
элементы директории (файлы и непосредственные поддиректории): Имя директории
соединяется с именем содержащегося в ней элемента через разделитель
/
имён файлов и отдаётся на обработку этой самой рекурсивной
процедуре. Наконец, особый случай: имя не относится к существующему
объекту, тогда процедура ворчит и завершает работу.
В этом словесном описании алгоритма имеются некоторые пробелы. Самая главная
неприятность нас ждёт при обработке имени директории. Даже пустые директории не
совсем пустые: в них, как минимум, содержатся два объекта: .
и ..
.
Первый из них, напомним, является альтернативным именем самой директории,
второй — синоним директории, расположенной «этажом выше» в файловой иерархии.
Наличие этих объектов (в первую очередь второго) обеспечивает связность
файловой системы: каждая директория «знает», в какой директории она содержится.
Лишь в корневой директории оба объекта — .
и ..
—
указывают на неё саму (это не позволит подняться в иерархии выше корня).
Теперь представим себе, как поведёт себя наша рекурсивная процедура, получив
как параметр имя директории, скажем, /etc
. Поскольку получено имя директории, в цикле
обрабатываются все находящиеся в ней объекты. Список этих объектов начинается,
естественно, с директории .
. Поэтому
дальше следует рекурсивный вызов для имени /etc/.
. Что же будет обнаружено там? Конечно же,
опять .
. Последует рекурсивный вызов для
/etc/./.
, затем для /etc/././.
, затем для /etc/./././.
, и так далее. В какой-то момент Perl
пожалуется на глубокую рекурсию, а спустя некоторое время уйдёт в себя. До
обработки /etc/..
дело даже не дойдёт,
но если бы дошло, нас ждал бы не меньший удар. Такого нельзя допустить, поэтому
директории .
и ..
должны обрабатываться особым образом.
Подобную проблему могут создать символические ссылки на директории. Что должна
делать программа, обнаружив такую ссылку? Вывести только имя ссылки или также
и содержимое той директории, на которую указывает ссылка? Во втором случае
возможна ситуация, когда, к примеру, символическая ссылка указывает на корень
файловой системы, и тогда мы получаем ту же проблему, что и с директорией
.
. Заметим, что следование по
символическим ссылкам не позволяет, вообще говоря, считать структуру директории
древовидной. Так что при обнаружении символической ссылки будем поступать с ней
как с обычным файлом.
Есть ещё одна маленькая трудность. При обработке имени корневой директории
/
каждое выведенное имя будет начинаться
с //
(имя директории плюс разделитель).
Этот особый случай нуждается в особом подходе.