Поиск

Краткий обзор

А теперь перейдем к краткому рассмотрению некоторых модулей, входящих в стандартную поставку Perl и уже установленных на вашем компьютере.

Исследование файлов и каталогов

На 10-м занятии, "Файлы и каталоги", вы узнали, как открывать каталоги и читать списки имен файлов, содержащихся в этих каталогах. Тогда же попутно встал вопрос о чтении подкаталогов, но мы не стали углубляться в него. Теперь настало время узнать, как без особых усилий выполнить рекурсивное чтение каталога и его вложенных подкаталогов.

Можно написать простую программу поиска файла, о котором неизвестно точно, в каком каталоге он находится. Например, вам нужно найти файл с именем important.doc, который находится в одном из подкаталогов каталога documents, имеющего следующую структуру:

На этом рисунке показана структура каталогов, которые находятся в родительском каталоге с именем documents. Найти файл, расположенный в одном из подкаталогов каталога documents, с помощью команд opendir/readdir/closedir непросто. Сначала нужно провести поиск по каталогу documents. Затем нужно продолжить поиск по всем подкаталогам, входящим в documents, — accounting, misc и personal, а потом по всем каталогам, содержащимся в этих каталогах, и т.д.

Это старая проблема, которую программисты решают снова и снова на протяжении последних 30 лет. Писать собственное решение данной проблемы — значит зря терять время. Как и следовало ожидать, разработчики Perl нашли простое решение, реализованное в модуле File::Find. Чтобы использовать этот модуль в программе, просто поместите в нее (желательно, в начале) следующий код:

В результате вам станет доступна новая функция с именем find. Ее синтаксис выглядит следующим образом:

Второй аргумент данной функции — это список каталогов, по которым проводится поиск. А первый аргумент — новый для вас, это ссылка на подпрограмму. Ссылка на подпрограмму создается точно так же, как и ссылка на скаляр или массив: это просто имя подпрограммы, перед которым стоит обратная косая черта. Чтобы сделать ссылку на подпрограмму, перед ее именем вы должны поставить символ &.. Подпрограмма, имя которой указано в качестве первого параметра в функции find, будет вызываться для каждого файла и каталога из списка dirlist.

В листинге 14.1 приведена программа поиска пропавшего файла important.doc.

Проведем анализ программы.

  • Строки 1—2. С этих двух строк начинается практически любая программа на Perl. Ключ -w активизирует режим вывода предупреждений, а оператор use strict используется для ужесточения контроля интерпретатора Perl над ошибками в программе и выявления плохого стиля программирования.
  • Строка 3. К вашей программе подключается модуль File::Find. В результате становится доступной функция find.
  • Строка 5. Эта функция вызывается для каждого файла и каждого подкаталога, находящихся в каталоге /documents. Например, если в этом каталоге содержится 100 файлов и 12 подкаталогов, то подпрограмма wanted будет вызываться 112 раз.
  • Строка 6. Когда вызывается функция wanted(), переменная $File::Find::name будет содержать полный путь к текущему обрабатываемому файлу, а переменная $_ — только имя файла. В этой строке определяется, является ли искомым именем файла important.doc; и если да, то печатается полный путь к этому файлу.
  • Строка 10. При вызове функции find ей передается ссылка на подпрограмму Uwanted и имя каталога, в котором производится поиск файла. Функция wanted () вызывается для каждого файла и каталога, содержащихся в /documents.
  • Для функции, вызываемой find, будут доступны следующие переменные:
    $File::Find::name— полный путь к текущему файлу (каталог и имя файла);
    $File::Find::dir — имя текущего каталога;
    $ — имя текущего файла (без указания каталога). Очень важно, чтобы вы не меняли значение переменной $_ в своей функции. Если же вы сделали это, то не забудьте в конце произвести обратную замену.

В листинге 14.2 приведен еще один пример использования модуля File::Find. В этом примере удаляются все файлы с расширением .tmp с устройств С: и D:. Эти временные файлы имеют свойство накапливаться и "съедать" свободное пространство жесткого диска. Данную программу можно легко адаптировать для удаления файлов на компьютере, на котором установлена система UNIX, или для выполнения любых других функций по обслуживанию файловой системы.

Программа, приведенная в листинге 14.2, в основном аналогична программе из листинга 14.1. Проведем анализ ее отличий.

  • Строка 7. Проводится проверка файла, имя которого передано подпрограмме wanted, для выяснения, обычный ли это файл или каталог. Как вы помните, данная подпрограмма вызывается и для файлов, и для каталогов.
  • Строки 9—11. Проводится проверка имени файла для выяснения того, содержится ли в конце его расширение .tmp. И если да, то данный файл удаляется с помощью команды unlink.
Копирование файлов

Еще одну распространенную задачу — копирование файлов — можно выполнить в Perl довольно сложным путем.

  1. Откройте исходный файл для чтения.
  2. Откройте выходной файл для записи.
  3. Прочитайте исходный файл и выполните запись его содержимого в выход
    ной файл.
  4. Закройте оба файла.

И, конечно, после каждого шага вы должны убедиться в том, что ликаких ошибок не произошло и что каждая операция записи была выполнена успешно, А теперь позвольте мне показать вам более простой способ. В Perl предусмотрен модуль File::Copy, осуществляющий копирование файлов. Ниже приведен пример использования этого модуля.

Этот фрагмент кода копирует содержимое исходного файла sourcefile в выходной файл destination. Функция сору возвращает значение 1 в случае успешного завершения операции и 0, если возникла какая-то проблема, При этом переменной $! присваивается текст соответствующего сообщения об ошибке.

В модуле File::Copy предусмотрена также функция move. Путем простого изменения структуры элементов каталога файловой системы функция move пытается выполнить операцию перемещения файлов без физического переноса их содержимого. Если исходный и выходной файлы расположены в одном каталоге и имеют разные имена, выполняется обычная операция переименования файлов. Обычно функция move так работает в случае, когда оба файла находятся в одной файловой системе йля на одном диске. Если же по какой-либо причине выполнить быструю операцию перемещения файлов не удается, функция move сначала копирует исходный файл в выходной, а затем удаляет первоначальный файл. Рассмотрим следующий пример:

Данный фрагмент кода перемешает файл important.doc из его текущего каталога в каталог d:/archives/documents. Если при выполнении функции move произошел сбой, то в выходном каталоге возможно появление частично скопированного файла important.doc. В случае неудачного завершения операции move функция unlink удаляет частично скопированный файл из выходного каталога.

Ау! Есть здесь кто-нибудь?

Модули Perl не ограничиваются только выполнением операций над файлами и каталогами. Например, модуль Net::Ping можно использовать для определения того, может ли компьютер нормально взаимодействовать с другим узлом сети.

Имя модуля Net: :Ping происходит от утилиты ping системы UNIX. Эта утилита получила свое имя от слова "ping", обозначающего акустический импульсный сигнал, который используется на подводных лодках для обнаружения объектов по принципу отражения звука. Утилита ping посылает некоторый пакет другому компьютеру в сети. Если этот компьютер включен и нормально функционирует, то он посылает ответ, и команда ping сообщает об успешном выполнении операции. Модуль Net::Ping, при1 мер использования которого приведен ниже, работает аналогично.

Как видно из данного фрагмента кода, в модуле Net::Ping предусмотрена функция с именем pingecho. У этой функции два аргумента. Первый — это имя узла сети, который нужно проверить на работоспособность (в данном случае — www.yahoo.com). Второй аргумент указывает, как долго (в секундах) функция pingecho должна ждать ответа.

Из-за особенностей языка Perl для систем Windows 95/98/NT к моменту написания данной книги (лето 1999) модуль Net::Ping не работал. Этот модуль зависит от функции alarm, которая не работает в системе Windows, Однако фирма Асtivestate — главный разработчик Perl для системы Windows — объявила о планах реализации многих недостающих функций для Windows и о внесении соответствующих изменений в Perl.
Еще раз, пожалуйста, но по-английски!

Модуль English позволяет обращаться к некоторым специальным переменным Perl по их более понятным именам, как показано в следующем примере:

В приведенном фрагменте кода конструкция while{<>) обычно считывает одну строку текста из потока STDIN и присваивает его переменной $_. В нашем примере по-прежнему все так и происходит. Но при использовании оператора use English к переменной $_ можно обращаться по имени $ARG. Ниже приведен частичный список специальных переменных и их английских эквивалентов.

Полный список специальных переменных и их английских эквивалентов можно найти в электронной документации к модулю English.

Дополнительные средства диагностики

Модуль diagnostics языка Perl помогает находить ошибки в программе. Если по ходу чтения вы будете учиться языку, набирая примеры из данной книги, то наверняка будете получать от интерпретатора Perl сообщения об ошибках, которые не сможете понять до конца. Например, короткая программа

заставит Perl выдать следующее предупреждающее сообщение:

Благодаря модулю diagnostics Perl выдает подробные предупреждения и сообщения об ошибках. Можно изменить приведенную выше программу-образец, включив в нее модуль диагностики следующим образом:

В результате такого изменения программы и использования модуля диагностики будет выдано более подробное сообщение:

Если вы немного поразмышляете об этих двух сообщениях, то станет ясно, что они связаны. Первое сообщение очевидно. Perl говорит о том, что направлять письма по электронной почте нужно по адресу help\@support.com. Теперь, после данного объяснения, второе сообщение становится более понятным. Так как была активизирована команда use strict, переменную @support следовало объявлять с помощью mу. Но дело в том, что @support — не переменная, а часть электронного адреса, которая просто была неправильно интерпретирована Perl.

Буква перед сообщением указывает на тип ошибки. (W) обозначает предупреждение, (D) говорит об использовании устаревшего и не рекомендуемого синтаксиса, (S) — это строгое предупреждение, a (F) — это серьезная ошибка. Программа на Perl продолжает работу при выдаче всех типов сообщений, за исключением (F).

В документации по Perl предусмотрено около 60 страниц описания сообщений об ошибках. Если вам трудно понять, что означают краткие сообщения Perl об ошибках, то иногда разобраться в них поможет команда use diagnostics.

Полный список сообщений об ошибках и диагностической информации можно найти в разделе perldiag электронной документации по Perl.