Поиск

Получение листинга каталога

Прежде чем выполнять операции с каталогом, необходимо создать его дескриптор. Дескриптор каталога напоминает дескриптор файла с тем лишь отличием, что при чтении считывается не файл, а содержимое каталога. Для открытия дескриптора каталога используется функция opendir:

В этом синтаксисе вместо имени_дескриптора следует указать дескриптор каталога, который вы хотите открыть, а вместо имени каталога путь к каталогу, содержимое которого нужно прочитать. Если по какой-либо причине каталог нельзя открыть (например, у вас отсутствуют права на доступ к нему, либо просто указано имя несуществующего каталога), функция opendir возвращает значение false. При выборе имени дескриптора каталога следует руководствоваться теми же правилами, что и при выборе имени дескриптора файла (о них шла речь на 2-м занятии, "Строительные блоки Perl: числа и строки"). Всегда набирайте имя дескриптора прописными буквами, чтобы исключить возможный конфликт имен с ключевыми словами Perl, которые появятся в будущих версиях этого языка программирования. Вот пример использова-ния функции opendir: .

Во всех примерах этого занятия в качестве разделителя пути мы используем косую черту (/), как это принято в системе UNIX. Причина заключается в том, что такой стиль написания улучшает читабельность листингов (поскольку нет конфликтов со служебным символом Perl \) и не нарушает работоспособность программ в системе Windows.

После открытия каталога доступ к его содержимому можно получить с помощью функции readdir:

В скалярном контексте функция readdir возвращает следующий по порядку элемент каталога или значение undef, если достигнут конец каталога. В контекстесписка функция readdir возвращает все оставшиеся элементы каталога. Имена, возвращаемые данной функцией, могут относиться как к файлам, так и к каталогам, а в системе UNIX — еще и к специальным файлам. Порядок их следования соответствует физическому расположению в каталоге. Другими словами, элементы каталога никак не сортируются. Кроме того, функция readdir возвращает еще два специальных элемента каталога: . и .., которые соответствуют текущему и родительскому каталогам. В элементы каталога не включается путь.

После завершения работы с каталогом его дескриптор следует закрыть с помощью функции closedir:

В следующем примере продемонстрирована методика чтения каталога:

Здесь все содержимое каталога помещается в массив @FILES. Однако, чаще всего, из этого списка нужно исключить некоторые имена, например. И ..., поскольку для пользователя в них нет особого смысла. Для этого следует воспользоваться таким оператором чтения каталога:

В этом примере регулярное выражение /^\.\.?$/ соответствует строке текста, в которой находится как минимум одна точка. Функция grep отфильтровывает такие строки, поскольку перед регулярным выражением стоит оператор отрицания. Если нужно отобрать элементы каталога, содержащие заданное расширение, оператор чтения каталога будет выглядеть так:

Имена файлов, возвращаемые функцией readdir, не содержат путь, который мы указывали в функции opendir при открытии каталога. Поэтому следующий пример, скорее всего, не будет работать:

Кроме случаев, когда вы запускаете данную программу из каталога /tmp, при выполнении оператора open(FILEH, $file) будет возникать ошибка. Причина состоит в том, что программа читает список файлов каталога /tmp, а оператор open пытается открыть файл в текущем каталоге. Естественно, что если текущим является не каталог Дтр и имена файлов текущего каталога и каталога /tmp не совпадают, то функция open не будет находить файлы. Для решения проблемы в операторе open следует указать полный путь к файлу. Правильный код будет выглядеть так:

Отбор файлов заданного типа

Существует еще один метод получения списка нужных файлов заданного каталога, который называется отбором файлов (globbing). Если вы хоть немного работали с командной строкой DOS, то наверняка вам приходилось вводить команды наподобие dir *.txt. В данном случае команда dir выводит список всех файлов, имена которых имеют расширение *.txt. В UNIX понятие расширения файла отсутствует, однако отбор нужных файлов также можно осуществить с помощью командной оболочки. Например, аналог приведенной выше команды dir в UNIX выглядит так: Is *.txt. В результате будет получен список всех файлов, имена которых оканчиваются суффиксом .txt.

В Perl также предусмотрен специальный оператор glob, выполняющий описанные действия. Его синтаксис выглядит так:

glob шаблон

Здесь параметр шаблон соответствует именам файлов, которые необходимо отобрать. Он может содержать путь, а также часть имени файла. Кроме того, в шаблоне может быть указано несколько специальных символов, описанных в табл. 10.1. В контексте списка оператор glob возвращает имена всех файлов и каталогов, имена которых соответствуют шаблону. В скалярном контексте при каждом вызове описываемый оператор возвращает имя следующего файла, соответствующего шаблону.

Не путайте шаблоны оператора glob с регулярными выражениями. Учтите, что это не одно и то же.

Это замечание относится к приверженцам UNIX. В операторе Perl glob реализована методика отбора файлов, принятая в оболочке С. Она немного отличается от методики, используемой в оболочке Bourne (или Коrn). Данное замечание справедливо для всех платформ UNIX, на которых установлен Perl, независимо оттого, в какой оболочке работает конечный пользователь. И хотя методики отбора файлов во многом схожи, отличия все же есть, главным образом в способе интерпретации символов шаблона * и ?. Поэтому будьте внимательны.

Ниже приведено несколько примеров отбора файлов.

Ниже приведен список основных отличий функции glob от opendir/readdir/closedir.

  • Функция glob может возвращать только ограниченное количество файлов. Если в каталоге будет находиться большое количество файлов, эта функция, скорее всего, аварийно завершит свое выполнение. Причина состоит в том, что в текущей версии Perl функция glob реализована с помощью сценария оболочки С, который может возвращать только ограниченное количество файлов. При использовании функций opendir/readdir/closedir подобная проблема не возникает.
  • Функция glob возвращает имя файла вместе с путем, который указан в шаблоне, тогда как функции opendir/readdir/closedir возвращают только имя файла. Например, оператор glob( '/usr/include/*.h') к каждому возвращаемому имени файла добавляет путь /usr/include/.
  • Функция glob работает медленнее, чем opendir/readdir/closedir. Причина очевидна. Perl должен запустить внешнюю программу, которая выполнит отбор и сортировку файлов, а затем получить от нее данные и интерпретировать их.

Итак, исходя из этого, какими же средствами лучше всего воспользоваться для отбора файлов? Ответ один — теми, которыми вам удобнее. Однако стоит иметь в виду, что использование opendir/readdir/closedir позволяет создать более универсальный и гибкий код. Поэтому в большинстве примеров мы используем именно набор функций opendir/readdir/closedir.

Для полноты картины стоит упомянуть еще об одном способе отбора файлов. Просто поместите шаблон в угловые скобки (о); в результате угловой оператор превратится в некое подобие оператора glob, например:

Синтаксис углового оператора для отбора файлов является устаревшим, к тому же он может ввести в заблуждение кого угодно, поэтому им пользоваться не рекомендуется. Для ясности в примерах из данной книги мы пользовались оператором glob.