Россия, Москва |
Введение в программирование на Perl
Файлы, каталоги, конвейеры, сокеты
При выполнении различных операций в CGI-скриптах часто приходится работать с файловой системой Web-узла. Perl обеспечивает довольно разнообразный инструментарий для такой работы. Условно его можно разбить на четыре части:
- работа с файлами;
- работа с каталогами;
- работа с каналами (конвейеры);
- работа с сетевыми ресурсами через сокеты.
Файлы
Для работы с файлами в Perl применяют несколько встроенных функций. Открывают файл функцией open. При этом для указания действий с записями файла используют префиксы:
open IN,"<test.txt"; # открыть файл на чтение open IN,">test.txt"; # открыть файл на запись open IN,"+<test.txt"; # открыть файл на чтение и запись open IN,">>test.txt"; # открыть файл на модификацию (добавление)
Закрывают файл при помощи функции close:
close IN;
Для чтения записей из файла используют либо чтение потоком — <file>, либо функцию read:
while(<IN>) { print $_; } read STDIN, $query, $ENV{CONTENT_LENGTH};
В первом случае данные построчно считываются из файла IN и распечатываются в поток стандартного вывода. Во втором случае из потока стандартного ввода, который тоже является файлом, считывается функцией read $ENV{CONTENT_LENGTH} байтов. Для обработки потока стандартного ввода CGI-скрипта подходит только второй способ, так как сервер не закрывает потока ввода, что приводит к бесконечному ожиданию ввода и разрыву соединения по timeout.
Каталоги
Для работы с каталогами используют ряд встроенных функций: opendir, readdir, closedir. Первая открывает дескриптор файла каталога, вторая позволяет читать записи из файла каталога, третья закрывает дескриптор файла каталога:
opendir DIR,"/usr/user"; while($_=readdir(DIR)) { next if -d; print $_; } closedir DIR;
В данном примере распечатываются названия файлов из каталога "/usr/users". При этом имена каталогов не распечатываются. Такое поведение скрипта определяется модификатором if в операторе next.
Каналы
Одним из замечательных свойств командных языков является возможность использования конвейеров. Они организуются путем перенаправления стандартного потока вывода одной программы в стандартный поток ввода другой. Иногда следует некоторые данные из скрипта профильтровать через такой конвейер, а потом снова ими воспользоваться. Открыть файл оператором типа
open FILTER,"<cat;more>";
нельзя. С точки зрения логики, при такой форме записи один дескриптор файла будет связан с разными потоками. Кроме того, поток стандартного ввода организует прием данных от сервера и уже занят. Возможность использования таких фильтров в Perl обеспечивает библиотека IPC. Запись при этом должна выглядеть следующим образом:
#!/usr/local/bin/perl use IPC::Open2; use FileHandle; $pid = open2(\*RDR,\*WRD,"cat"); WRD->autoflush(); print WRD "test\n"; $got = <RDR>; print "Это \$got:$got";
В данном случае для открытия канала связи между процессами используется функция open2. Ей необходимы три аргумента: указатель на дескриптор потока для чтения ( \*RDR ), указатель на дескриптор потока для записи ( \*WRD ) и строка внешней программы-фильтра ( "cat" ). Вместо cat можно указать любое множество команд, организованных в виде конвейера. Команда print посылает данные в этот конвейер, а команда "" считывает из него данные.
Работа с серверами Internet. Сокеты
Пусть и не очень часто, но все-таки приходится при разработке CGI-скриптов пользоваться возможностью обращения из скрипта к серверу, установленному на другом компьютере. При работе в сетях TCP/IP для этой цели используют библиотеку сокетов (Berkeley sockets) TCP/IP. Сокет — это пара "IP-адрес — номер порта". При программировании речь, конечно, будет идти о структуре данных, которая позволяет передать/принять данные с удаленного компьютера.
IP-адрес в сокете отвечает за доступ к определенному компьютеру в Internet. В общем случае IP-адреса закрепляются не за отдельными компьютерами, а за сетевыми интерфейсами. Один сетевой интерфейс может иметь несколько IP-адресов. Эта возможность используется при организации виртуальных Web-узлов.
Порт — это виртуальный канал приема/передачи данных, в котором происходит обслуживание приходящих из сети запросов. Этот канал обозначается цифрой. Первые 256 каналов — это WKS (Well Known Services, "хорошо известные" сервисы). Например, за HTTP-обменом закреплен 80-й порт, за FTP-обменом – 20-й и 21-й порты, за DNS (Domain Name System, служба доменных имен) – 53-й порт и т.д.
Порты бывают двух типов, TCP и UDP (User Data Protocol, пользовательский протокол данных), т.е. соответствуют типу транспортного протокола, который используется для передачи данных. Мы будем рассматривать в качестве примера именно TCP, т.к. HTTP-обмен использует только TCP-порты. Слово "порты" во множественном числе здесь употребляется не случайно. Дело в том, что только первоначальное обращение к HTTP-серверу происходит по 80-му порту. Для того чтобы обслуживать много запросов практически одновременно, сервер переназначает порт, и клиент, например, браузер, общается с сервером уже по другому порту.
Кроме TCP/IP существуют еще и другие виды сокетов, поэтому при программировании нужно указывать тип сокета. Ниже приведен пример обращения из скрипта к серверу HTTP:
#!/usr/local/bin/perl use IO::Socket; $remote = IO::Socket::INET->new( Proto=>"tcp", PeerAddr=>"localhost", PeerPort=>"80" ) or die "No service"; $remote->autoflush(1); print $remote "HEAD / HTTP/1.0\n\n"; while() { print; } -close $remote;
В данном примере используется пакет Socket из библиотеки IO (Interpretive Operation, работа в режиме интерпретации) (оператор use ). В третьей строчке примера происходит открытие дескриптора для сокета. Затем этот дескриптор применяется как обычный дескриптор файла, который открыт для записи и чтения.
Прежде чем писать данные в сокет, обычно отменяют буферизацию ( autoflush(1) — выталкиваем по одному байту). Делается это для того, чтобы данные сразу уходили на сервер. В противном случае сервер может разорвать соединение, не дождавшись запроса.
Оператор print демонстрирует запрос по протоколу HTTP 1.0 к HTTP-серверу. В этом запросе просто считывается документ из корня каталога HTTP-сервера. Обычно это файл index.html. Чтение отклика происходит в цикле while. После считывания отклика socket закрывается по функции -close.
Отложенное исполнение. Операция eval
Интерпретируемые языки программирования имеют две особенности, которых нет у компилируемых языков:
- рекурсия;
- отложенное исполнение.
С точки зрения эффективности кода это, конечно, не лучшие решения. Рекурсия может приводить к различного рода переполнениям, что, в свою очередь, вызывает крах программы, а отложенное исполнение не позволяет заранее оптимизировать код и в ряде случаев может приводить к рекурсивному вызову интерпретатора. Тем не менее изящность этих механизмов не может оставить программиста равнодушным.
Отложенное исполнение в Perl применяется в случае подстановки, в том числе рекурсивной
$sm =~ s/%(.{2})/pack('c',hex($1))/eg;
и в случае использования операции eval. Алгоритм работы этой операции достаточно прозрачен. В качестве аргумента в eval передается строка, которая затем рассматривается как Perl-программа. Для программирования из Web-браузера на Perl можно написать, например, такой скрипт:
#!/usr/local/bin/perl read STDIN,$query,$ENV{CONTENT_LENGTH}; $query =~ s/%(.{2})/pack('c',hex($1))/ge; $query =~ tr/+/ /ge; $query =~ s/f=//; eval $query;
Функция read в этом примере обеспечивает считывание данных из потока стандартного ввода скрипта. Операция рекурсивной подстановки позволяет преобразовать текст программ из формата form-urlencoded в ascii. Функция транслитерации tr заменяет символ "+" пробелом. Операция подстановки, следующая за транслитерацией, удаляет из кода программы имя поля и символ "=". Операция eval позволяет исполнить этот код. При этом предполагается, что данные передаются из формы с единственным полем типа textarea с именем "f".
Справедливости ради следует заметить, что в операции транслитерации кроме символа "+" нужно указать еще и другие символы, которые могут оказаться в коде программы. Например, если ввод кода осуществляется из браузера в среде MS-Windows, то при каждом переводе строки будет генерироваться символ "0D", который интерпретатор Perl для среды Unix не распознает.
Библиотеки
Программировать на Perl без применения дополнительных библиотек довольно сложно. В сети существует библиотека CPAN( http://www.perl.com/CPAN/ ). CPAN — это огромное хранилище полезного программного обеспечения. Для программирования CGI-скриптов в этой библиотеке имеются модули CGI-программирования. В последних версиях вместе с дистрибутивом Perl поставляется модуль CGI.pm. Но даже если этого модуля нет, его просто нужно скачать из CPAN.
Установка модуля в Perl довольно проста. Требуется выполнить следующие действия:
- модуль в архивированном виде переписывается из CPAN:
edu>ftp ftp.perl.com
- создается временный каталог и модуль разархивируется в него:
edu>gzip -d имя_модуля
- в каталоге находят файл Makefile.pl и выполняют генерацию файлов
для сборки модуля:
edu>perl Makefile.pl
- затем выполняют команды:
edu>make; make test; make install
Теперь модуль установлен в Perl-каталог, и на него настроены все пути. С этого момента программы могут использовать функции из установленного модуля.
Включать функции из модуля в программу на Perl можно при помощи оператора use. Например, для получения имени текущего каталога используют функцию getcwd() из библиотеки Cwd:
#!/usr/local/bin/perl use Cwd; $dir = getcwd(); opendir DIR,$dir; .... closedir DIR;
При организации конвейеров также применяются модули из библиотеки. Стандартные библиотеки обычно поставляются вместе с дистрибутивом Perl и устанавливаются при его сборке.