Для получения списка имен из заданного каталога также можно воспользоваться дескриптором каталога. Дескрипторы каталога очень похожи на файловые дескрипторы – как по внешнему виду, так и по поведению. Их тоже необходимо открыть (opendir вместо open), прочитать из них данные (readdir вместо readline), а затем закрыть (closedir вместо close). Но вместо чтения содержимого файлов читаются имена файлов (и прочая информация о них). Пример:
my $dir_to_process = "/etc";
opendir DH, $dir_to_process or die "Cannot open $dir_to_process: $!";
foreach $file (readdir DH) {
print "one file in $dir_to_process is $file\n";
}
closedir DH;
Дескрипторы каталогов, как и файловые дескрипторы, автоматически закрываются в конце работы программы или при их повторном открытии для другого каталога. В отличие от глобов, для которых в старых версиях Perl порождался отдельный процесс, дескриптор каталога никогда не создает новый процесс. Это повышает их эффективность в приложениях, выжимающих из компьютера все вычислительные ресурсы. Однако операции с ними выполняются на более низком уровне, а это означает, что программисту придется выполнять большую часть работы самостоятельно. Например, имена файлов возвращаются без упорядочения1, а в список включаются все файлы, а не только те, которые соответствуют заданному шаблону (например, *.pm, как в примере с глобами). В частности, в него включаются файлы с точкой и специальные записи . и ...2 Таким образом, если нас интересуют только файлы с расширением pm, в цикл включается функция пропуска файлов:
while ($name = readdir DIR) {
next unless $name =~ /\.pm$/;
... Продолжение обработки ...
}
Обратите внимание: в данном случае используется синтаксис регулярного выражения, а не глоба. В этом примере отбираются все файлы, не начинающиеся с точки:
next if $name =~ /^\./;
А если, скажем, нас интересуют все файлы, кроме специальных записей . (текущий каталог) и .. (родительский каталог), это можно выразить так:
next if $name eq "." or $name eq "..";
Мы переходим к той части, в которой многие программисты путаются, так что будьте внимательны. Имена файлов, возвращаемые оператором readdir, не содержат пути. Возвращаются просто имена файлов в каталоге. Таким образом, вы получаете не /etc/passwd, а passwd. (В этом проявляется еще одно отличие от операций с глобами, отсюда и возникают недоразумения.) Итак, для получения полных имен необходимо выполнить дополнительную обработку результатов:
opendir SOMEDIR, $dirname or die "Cannot open $dirname: $!";
while (my $name = readdir SOMEDIR) {
next if $name =~ /^\./; # Пропустить файлы, начинающиеся с точки
$name = "$dirname/$name"; # Добавить путь
next unless -f $name and -r $name; # Только файлы с доступом для чтения
...
}
Без обработки операторы проверки файлов будут проверять файлы в текущем каталоге, а не в том каталоге, имя которого хранится в $dirname. Это самая распространенная ошибка при использовании дескрипторов каталогов.