3: Введение в программирование на Perl

Bash CGI

3: Введение в программирование на Perl

Язык программирования Perl является основным средством разработки CGI-скриптов для Web-узлов. Его не применяют только там, где требуется высокая эффективность кода и нет стандартных библиотек для Perl.

Perl как язык разработки скриптов имеет ряд преимуществ. Перечислим их в порядке значимости:

  • независимость от программно-аппаратной платформы;
  • мощные средства разбора строк;
  • простота работы с переменными окружения;
  • простота работы со входными и выходными стандартными потоками;
  • возможность чтения заданного числа символов из входного потока;
  • хешированные таблицы;
  • возможность организации конвейеров;
  • библиотеки TCP/IP-обмена;
  • множество стандартных библиотек прикладных программ.

Все это делает программное обеспечение, написанное на Perl, мобильным, а разработку программ — быстрой и простой.

Структура Perl-программы

У программы на языке Perl нет жестко заданной структуры. Точнее сказать, программист не обязан ее соблюдать. Если же он захочет, чтобы интерпретатор контролировал объявление и использование переменных и конструкций языка, то при помощи класса strict он может такой контроль установить.

Программа на Perl состоит из операторов языка, которые должны заканчиваться символом ";". Например:

print "Привет, Perl.";
 

В общем случае операторы делятся на простые и составные. Простой оператор — это оператор print, например, а составной — while():

while() {s/</&lt;/g; s/>/&gt;/g; print $_;};
 

Программа на Perl исполняется интерпретатором Perl. Есть и компиляторы с этого языка, но они используются реже. При программировании CGI-скриптов в Unix интерпретатор вызывается из того же файла, который содержит программу, например:

#!/usr/local/bin/perl
print "Привет, Perl.";
 

В данном случае первая строка — это вызов интерпретатора с указанием полного пути к нему от корневого каталога файловой системы.

Вообще говоря, символ "#" — это символ начала комментария в Perl. Последовательность символов от символа "#" до конца строки рассматривается как комментарий. Часто в качестве комментария используют целые строки. В этом случае символ "#" ставится в первой позиции строки:

#!/usr/local/bin/perl
#
#Печатаем HTML-заголовок
#
print "Content-type: text/html\n\n";
#
#Содержание документа
#
print "Perl и CGI.";
 

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

В последнее время основным рабочим местом авторов HTML-страниц и CGI-скриптов стали системы на платформе Windows. Как известно, конец строки в Windows и в Unix обозначается разными последовательностями неотображаемых символов. Если автор использует текстовый редактор в Windows, а потом как binary копирует файл в Unix, то эти символы передаются. Сервер начинает сообщать об ошибках, которые автору не видны. В таком случае можно сделать следующее: либо копировать программы как char, тогда происходит перекодировка, либо использовать "умные" текстовые редакторы.

Удобна также и Samba, которая позволяет редактировать "по месту" из Windows в Unix.

Стандартный поток вывода

Основная цель создания CGI-скрипта — обработка данных запроса пользователя и формирование отклика сервера на этот запрос. Можно рассматривать и другие задачи, которые позволяют решать CGI-скрипты, но эта задача — главная. Скрипт должен формировать не просто отклик, а HTTP-отклик. Это означает, что он должен сформировать заголовок и тело HTTP-сообщения, которые отделяются друг от друга пустой строкой.

Проще всего реализовать такой отклик с помощью команды print:

#!/usr/local/bin/perl
print "Content-type: text/html\n\n";
print "<HTML><HEAD></HEAD><BODY>";
print "<H1>Perl и CGI.</H1>";
print "</BODY></HTML>";
 

Первая строчка определяет заголовок HTTP-отклика и пустую строку (два символа "\n\n"). Остальные операторы print формируют тело сообщения. Оператор print пишет в стандартный вывод список своих аргументов. Вообще говоря, print можно использовать для вывода данных в любой файл. Если имя файла не указано, то вывод осуществляется в стандартный поток вывода.

Переменные окружения

Скрипт порождается сервером в некоторой операционной среде. Эта среда называется окружением (environment). Частью окружения являются так называемые переменные окружения. При вызове скрипта их порождает и присваивает им значения HTTP-сервер. Список переменных определен спецификацией Common Gateway Interface.

В Perl существует встроенный системный массив переменных окружения   %ENV. Символ "%" перед именем массива означает, что это массив ассоциативный, т.е. значение элемента массива может быть выбрано путем указания ключа, с которым оно связано. Например, нужно определить метод доступа к скрипту:

#!/usr/local/bin/perl
print "Content-type: text/plain\n\n";
print "REQUEST_METHOD:$ENV{REQUEST_METHOD}";
 

В данном контексте символ "$" перед именем массива не должен вводить в заблуждение. Обращение происходит к элементу ассоциативного массива. Это скаляр, поэтому и применяется символ "$". Ключ, по которому выбирается значение, указан в фигурных скобках.

Очень полезен скрипт распечатки всех переменных окружения, которые переданы скрипту (perlenv2.htm). Обычно этот отчет получают при отладке HTML-форм:

#!/usr/local/bin/perl
print "Content-type: text/plain\n\n";
foreach $hkey (keys %ENV)
{
 print "$hkey:$ENV{$hkey}";
}
 

Этот простой скрипт распечатывает значения всех переменных окружения, используя цикл foreach. В этом цикле переменная цикла hkey пробегает по всем уникальным ключам (именам переменных окружения), которые доставляет функция keys.

Аргументы командной строки

Когда пользователь работает с операционной средой в режиме удаленного алфавитно-цифрового монитора, он пользуется услугами оболочки (shell). Команды в операционной среде в этом случае вводятся в командной строке, и за каждой из команд может тянуться шлейф аргументов. Эти аргументы и называются аргументами командной строки.

CGI-скрипт вызывается не из оболочки, а загружается HTTP-сервером. Если необходимо воспользоваться аргументами командной строки, то сервер должен породить данную командную строку тоже. В CGI это делается только для запросов ISINDEX. В таком случае скрипт вызывается через URI типа:

http://my.intuit.ru/directory/
     script?arg1+arg2+arg3
 

В этой записи arg1+arg2+arg3 — аргументы командной строки скрипта, т.е. данный URI равнозначен вводу в командной строке команды:

host>script arg1 arg2 arg3
 

Для приема этих аргументов достаточно воспользоваться скриптом типа:

#!/usr/local/bin/perl
print "Content-type: text/plain\n\n";
foreach $arg (@ARGV)
{
 print "$arg\n";
}

В данном случае мы просто их распечатаем. Программа будет выбирать по одному аргументу из системного массива аргументов командной строки @ARGV и помещать их в переменную $arg, а затем печатать.

В принципе, аргументы попадают и в переменную окружения   QUERY_STRING, т.к. при запросе типа ISINDEX применяется метод GET. Но тогда придется данную переменную разбирать. Аргументы командной строки уже сделали эту работу за программиста.

Стандартный ввод

Поток стандартного ввода обычно ассоциируется с клавиатурой терминала. Поток стандартного ввода — это источник входных данных по умолчанию. В С, например, поток стандартного ввода ассоциируется с файлом STDIN. В Perl применяется то же самое имя.

Для построчного чтения входного потока в Perl применяется пара символов "<>". Простая программа, читающая входной поток, может выглядеть следующим образом:

#!/usr/local/bin/perl
while()
{
 print $_;
}
 

В данном примере две скобки подряд определяют чтение из потока стандартного ввода. Магическая последовательность $_ обозначает системную переменную, в которую по умолчанию помещается каждая считанная из стандартного ввода строка. При этом символы конца строки сохраняются.

Согласно спецификации CGI, скрипт получает данные через стандартный ввод в том случае, если в качестве метода доступа в форме будет указан POST. Приведенный выше пример работы со стандартным потоком ввода не может быть применен для обработки такого запроса. Дело в том, что сервер не закрывает поток ввода и, следовательно, не передает EOF (End of file, конец файла). Цикл while в этом случае будет бесконечным.

Скрипт сам определяет точное количество байтов, которые он должен прочитать из стандартного ввода. Это можно сделать, прочитав значение переменной CONTENT_LENGTH (perlm1.htm):

#!/usr/local/bin/perl
print "Content-type: text/plain\n\n";
print "CONTENT_LENGTH=$ENV{CONTENT_LENGTH}";

Когда число байтов для чтения из потока стандартного ввода определено, эти байты нужно считать. Воспользуемся функцией read:

#!/usr/local/bin/perl
print "Content-type: text/plain\n\n";
print "CONTENT_LENGTH=$ENV{CONTENT_LENGTH}\n";
read STDIN,$query,$ENV{CONTENT_LENGTH};
print "Query:$query.";

После выполнения этих несложных операций в переменную $query будет занесено содержание стандартного ввода скрипта, с которым потом можно будет разбираться

Типы данных и переменные

В Perl существует довольно своеобразный набор типов данных: скаляры, одномерные массивы (массивы), ассоциативные массивы (хешированные таблицы или хеши), глобальные символы, ссылки. Обычно первых трех для CGI-программирования вполне хватает. Ссылки применяют в совокупности со стандартными библиотеками и вместо функции отложенного выполнения eval. Глобальные символы нужны при работе с файлами или для межпроцессного обмена данными при открытии каналов (потоков данных) через дескрипторы.

Скаляры

Скаляры — это все, что нельзя записать в виде массива или структуры. Числа всех типов и строки символов относятся к скалярному типу. Скаляр обозначается символом "$" перед именем. Примеры скаляров:

$a = 1;
$b = 2.5;
$str = "это строка символов";
  
Одномерные массивы

Массивы — это множество элементов, причем все элементы могут быть разнородными. В Perl нет понятия массива чисел или массива строк. Массив не может быть многомерным. Можно реализовать массив массивов и, таким образом, сделать массив двумерным. Массив обозначается символом "@" перед именем. Примеры массивов:

@a = (1,2,3);
@b = (4,5,6,2.5,"test");
@c = (@a,@b);
@in = 
$q = $c[3]; # 4
  

Индексирование элементов массива начинается с цифры 0. Поэтому в нашем примере четвертый элемент массива с будет индексироваться как $c[3].

Ассоциативные массивы

Ассоциативные массивы — это двухколоночная таблица. Первая колонка — ключ, а вторая колонка — связанное с ним значение. Ассоциативные массивы называют еще хешированными таблицами или просто хешами. Дело в том, что значение можно извлечь из таблицы прямо, указав ключ. При этом используется алгоритм хеширования по ключу.

Для краткости будем называть ассоциативный массив хешем. Обозначается переменная типа хеш символом "%" перед именем. Примеры хешей и обращений к их элементам:

%a = ("test",1,"test2",2);
$c = $a{test}; # $c=1
  

В первом случае хеш инициируется как обычный массив. Во втором примере мы выбираем значение определенным ключом "test". Результат этой операции, скаляр, помещается в переменную $c.

Указатели

Указатели — это аналог адресных указателей в С. Указатель обозначается символом "\" перед именем переменной:

$a = 1;
$p = \$a;
@b = (1,2,3);
$p = \@b;
$p = \%c;
  

Обращения к значениям, выбранным указателем, являются аналогом применения функции eval (отложенное исполнение):

%a = ("test",1,"test2",2);
$p = \%a;
$c = $$p{"test"};
  

где вместо "а" используется $p.

Глобальные символы

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

open IN,"
read IN,$p,500;
close IN;
  

В данном случае IN — дескриптор файла.

Регулярные выражения (сопоставление с образцом)

Самым интересным средством Perl является механизм регулярных выражений, сравнение с образцом и подстановки, которые он унаследовал от таких типичных для Unix-систем утилит, как awk и sed.

Операция сопоставления с образцом задается как "=~":

$query =~ /target/;
 

В данном случае в скаляре $query отыскивается подстрока "target". С операцией "=~" связано два действия по умолчанию. Во-первых, операция сопоставления в полной форме будет записана как

$query =~ m/target/;
 

То есть перед шаблоном используется префикс m. Во-вторых, если опустить $query, то образец будет сопоставляться с содержанием системной переменной $_:

/target/;
 

Операция "=~" возвращает значение "истина"(true), если образец был найден, и "ложь"(false), в противном случае. Из этого следует, что данную операцию можно использовать в операторах ветвления:

if($query =~ /target/)
{
print $query;
}
 

В данном случае, если образец содержится в $query, то запрос печатается, не содержится — пропускается при печати.

При сопоставлении с образцом одновременно осуществляется разбор запроса: шаблон попадает в переменную $_, часть строки до найденного шаблона — в $`, а после образца — $'. Но для программирования разбора данных из формы больше подойдет следующий пример (perlexp1.htm):

http://intuit.ru/scripts/
script?n1=v1&n2=v2&n3=v3
...
($v1,$v2,$v3) = ($query =~ 
/^n1=(.+)&n2=(.+)&n3=(.+)$/);
print $v1,$v2,$v3;
 

В переменные $v1 — $v3 будут записаны значения полей n1-n3, соответственно. При этом символ "^" означает, что весь шаблон должен стоять вплотную к началу строки. Символ "$" в конце шаблона означает, что шаблон должен распространяться на всю строку до конца. Символ "." означает любой символ, входящий в таблицу символов, "+" означает любое количество этих символов, но не менее одного.

Еще более мощным средством является подстановка, которая базируется на механизме сопоставления с образцом. Для ее осуществления следует использовать префикс s%

$query =~ s/intuit/ruru/;
 

В данном случае, если в строке будет найден шаблон intuit, он будет заменен на ruru. Наиболее очевидной областью применения подстановки является перекодировка запросов form-urlencoded:

$query =~ s/%(.{2})/pack('c',hex($1))/eg;
 

Спецсимволы и символы из второй половины таблицы ASCII при передаче трансформируются в шестнадцатеричные описания байтов (две цифры), следующие после символа "%".

Управление потоком вычислений

Под управлением потоком вычислений понимают способность программы в зависимости от условий выполнять те или иные части кода и повторять различные части кода. Для этой цели в Perl включены такие операторы как goto, next, last, redo, if, while, for, foreach и другие.

Оператор GOTO

Оператор goto позволяет перейти к исполнению оператора Perl, выделенного меткой, которая указывается в качестве аргумента оператора goto:

while()
{
if(/the end/) {goto out;};
}
out: print "the_end\n";
  

В данном случае выход из цикла осуществляется по оператору goto. Вслед за всеми современными учебниками, напомним, что использование goto — это плохой стиль программирования. Метка в Perl задается как строка, за которой вплотную следует символ двоеточия(":").

Оператор while

В нашем примере встретилось еще несколько конструкций управления потоком вычислений. Первая из них — оператор while. While определяет цикл, который исполняется до тех пор, пока значение выражения, указанного в качестве аргумента оператора while, — "истина". Пример:

while($line =~ /the_end/)
{
$line = <>;
print "No";
}
print "the_end\n";
  

В данном случае оператор goto не используется. Выход из цикла осуществляется по условию вхождения подстроки the_end в строку ввода. Вхождение проверяется путем сопоставления с образцом. Входная строка считывается из стандартного ввода (операция <>).

Оператор for

Оператор for — это традиционный оператор цикла. Типовая схема применения данного оператора может быть показана на следующем примере:

for($i=0;$i<CONTENT_LENGTH;$i++)
{
$query[$i] = getc;
}
$q = join (@query);
print $q;
  

Здесь данные в цикле считываются посимвольно из стандартного потока ввода в массив @query. Число символов, которые нужно считать, определяется переменной окружения CONTENT_LENGTH. После завершения ввода массив объединяется в строку $q при помощи функции join. Наиболее типичной ошибкой при использовании оператора for является отсутствие символа "$" у переменной цикла. Все три аргумента оператора for могут быть выражениями соответствующего типа.

Оператор foreach

Оператор foreach позволяет организовать цикл путем перебора элементов списка. В качестве такого списка можно использовать массив:

foreach $arg (@ARGV)
{
print $arg;
}
  

В данном случае переменная цикла $arg пробегает по всем аргументам командной строки скрипта, которые задаются встроенным массивом @ARGV. Аналогично можно пройти и по всем переменным окружения:

foreach $arg (keys %ENV)
{
print "$arg:$ENV{$arg}\n";
}
  

Отличие данного примера от предыдущего заключается в том, что переменные окружения представляют собой хеш. Поэтому сначала получаем массив ключей (имен переменных окружения), переменная $arg пробегает по этому массиву, и в цикле печатаются пары "имя переменной — значение переменной".

Оператор if

После goto и операторов цикла следует остановиться на операторе if. Данный оператор позволяет организовать ветвление программы или, как это еще называют, условное исполнение отдельных ее частей. Примером может служить скрипт обработки данных, которые могут доставляться как по методу GET, так и по методу POST:

if ($ENV{REQUEST_METHOD} =~ /POST/)
  {
  read STDIN,$query,$ENV{CONTENT_LENGTH};
  }
else
  {
  $query = $ENV{QUERY_STRING};
  }
  

В данном случае анализируется значение переменной окружения   REQUEST_METHOD. В зависимости от ее значения либо включается чтение данных из стандартного потока ввода, либо считывается значение переменной окружения   QUERY_STRING.

Оператор next

Если оператор goto не применять (концепция структурного программирования), то при работе с циклами нужно обрабатывать ситуации пропуска конца тела цикла и досрочного выхода из тела цикла. Пропуск операторов конца тела цикла осуществляется при помощи оператора next:

foreach $arg (@strings)
{
if(!($ENV{REMOTE_HOST} =~ /\.intuit\.ru/)) 
  next;
print $arg;
}
  

Здесь фрагмент скрипта печатает значения массива @string, только если скрипт вызван с компьютера из домена intuit.ru. Машина пользователя будет прописана в этом домене только в том случае, если она прописана в обратной зоне, т.е. есть не только прямое соответствие "имя — IP-адрес", но и обратное "IP-адрес — имя". При этом сервер должен выполнять поиск доменных имен компьютеров пользователей по их IP-адресам (режим nslookup).

Оператор last

Досрочный выход из тела цикла производится по оператору last. Обычно такой выход осуществляется по некоторому событию:

open IN,"<cont.txt";
while()
  {
  if($_ =~ /index\.htm/) last;
  #разбор строки
  }
close IN;
  

В данном примере скрипт считывает строки из файла счетчика посещений. Если в строке встречается имя файла "index.htm", то происходит досрочный выход из цикла.

Оператор redo

Оператор redo применяется для повторения последней итерации цикла. Например, если есть вложенные циклы while, которые выполняют одинаковые последовательности операторов, то внутренний из них можно заменить на оператор if с redo.

Файлы, каталоги, конвейеры, сокеты

При выполнении различных операций в 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 довольно проста. Требуется выполнить следующие действия:

  1. модуль в архивированном виде переписывается из CPAN:
    edu>ftp ftp.perl.com
     
  2. создается временный каталог и модуль разархивируется в него:
    edu>gzip -d имя_модуля
     
  3. в каталоге находят файл Makefile.pl и выполняют генерацию файлов для сборки модуля:
    edu>perl Makefile.pl
     
  4. затем выполняют команды:
    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 и устанавливаются при его сборке.