FAQ по perl и программированию для web <author>Составил Павел Аммосов, <tt/apavel@aha.ru/ <date>08 июня 2001 <abstract> Русскоязычный сборник часто задаваемых вопросов по языку программирования perl и server-side web programming. </abstract> <toc> <sect>Где взять перл и все с ним связанное<label id="Где взять перл и все с ним связанное"><p> <sect1>Где можно взять перл?<p> На CPAN == Comprehensive Perl Archive Network. Homepage CPAN -- http://www.cpan.org/ -- там берут все, связанное с перлом. Собственно исходники новейшей версии перла берут из файла http://www.perl.com/CPAN/src/latest.tar.gz (на самом деле с www.perl.com стоит редирект на ближайший, по мнению www.perl.com, mirror). <sect1>Где можно скачать перловскую библиотеку для работы с WWW?<p> Там же, где и все, относящееся к перлу -- на CPAN. Полный список всех модулей и библиотек -- <url url="http://search.cpan.org/" name="http://search.cpan.org/"> <sect1>C www.cpan.org слишком долго качается, нет ли где поближе?<p> Есть. Полный список зеркал находится в <url url="ftp://ftp.nluug.nl/pub/languages/perl/CPAN/MIRRORED.BY" name="ftp://ftp.nluug.nl/pub/languages/perl/CPAN/MIRRORED.BY">, там можно найти funet или что там поближе, или даже российские зеркала, например <url url="ftp://ftp.aha.ru/pub/CPAN" name="ftp://ftp.aha.ru/pub/CPAN"> <sect1>Я скачал модуль Lib::Module. Как его установить?<p> Обычно модуль приходит в формате tar+gzip, типа module-0.01.tar.gz. Вам необходимо его развернуть: tar zxf module-0.01.tar.gz и перейти в образовавшуюся директорию, например module-0.01: cd module-0.01 Обычно там находятся несколько файлов. Для вас будут важными следующие:<p> <descrip> <tag>README</tag> что это за модуль <tag>INSTALL</tag> как его поставить <tag>Makefile.PL</tag> перловый скрипт для генерации Makefile </descrip> Обычно инсталляция происходит следующим образом: <descrip> <tag><tt>perl Makefile.PL</tt></tag> генерация Makefile <tag><tt>make all</tt></tag>сборка модуля <tag><tt>make test</tt></tag>тестирование модуля <tag><tt>make install</tt></tag>инсталляция модуля </descrip> Необходимо внимательно смотреть, не возникли ли какие ошибки во время первых трех команд. Внимание: вы должны иметь административные привилегии для инсталлирование модуля в системе (обычно как root). Но, если вы их не имеете, то можете инсталлировать модуль у себя. Для этого вам необходимо выбрать, где это будет делаться. Для примера, в $HOME/lib/perl5. Необходимо создать эти директории, если их нет: <verb> mkdir ~/lib mkdir ~/lib/perl5</verb> Затем, при генерации Makefile для модуля, вы должны указать, где вы хотите инсталлировать модуль:perl Makefile prefix=$HOME Все остальные шаги без изменений. В результате вы получите альтернативное место, где у вас будут находиться модули. Например, у меня это выглядит так: <tscreen><verb> artur@disco:~ (681) ls -alFg ~/lib/perl5/ total 704 drwxr-sr-x 8 artur staff 512 Oct 06 15:53 ./ drwxr-sr-x 9 artur staff 1024 Oct 08 10:50 ../ drwxr-sr-x 4 artur staff 512 Mar 31 1998 aix/ drwxr-sr-x 2 artur staff 512 Aug 06 13:40 CPAN/ -r--r--r-- 1 artur staff 126951 Jul 24 19:37 CPAN.pm drwxr-sr-x 3 artur staff 512 Mar 31 1998 man/ drwxr-sr-x 31 artur staff 1024 Sep 09 15:41 site_perl/ </verb></tscreen> Для того, чтобы их использовать, есть несколько путей: <enum> <item> указывать при запуске perl в командной строке: <verb>perl -I$HOME/lib/perl5 -I$HOME/lib/perl5/site_perl script.pl</verb> <item> завести переменную шелла PERL5LIB: для sh (bash,zsh,ksh) <verb>PERL5LIB=$HOME/lib/perl5:$HOME/lib/perl5/site_perl</verb> export PERL5LIB для csh (tcsh) <verb>setenv PERL5LIB $HOME/lib/perl5:$HOME/lib/perl5/site_perl</verb> <item> указывать в начале скриптов дополнительные библиотеки: <verb> use lib "/home/you/lib/perl5"; use lib "/home/you/lib/perl5/site_perl";</verb> Данный случай подходит к CGI-скриптам, которые не наследуют ваших переменных окружения. </enum> <sect1>Как автоматизировать процесс установки библиотек и модулей?<p> Скачивание и установка вручную -- достаточно сложное занятие, требующее постоянного вмешательства с вашей стороны. Можно простым способом: сделать su (желательно), запустить <tt>perl -MCPAN -e shell</tt>. Если это первый запуск модуля CPAN, вам зададут серию вопросов на тему URL вашего любимого зеркала CPAN. После этого все просто: если вы знаете название модуля или библиотеки, то набираете, скажем, <tt>install Net::NNTP</tt>. Тогда этот модуль (для работы с NNTP) сам ищется на CPAN, сам скачивается, компилируется, устанавливается. Все, поставили, можно выходить и использовать вновь установленный модуль. Если название точно не известно, то набираем там же help и используем различные команды поиска типа i NNTP. <sect1>У меня нет root-привилегий. Можно ли использовать модуль CPAN для установки модулей в этом случае?<p> При использовании CPAN-модуля вам необходимо сконфигурировать его таким образом, чтобы он сам инсталлировал модули туда, куда вы укажете. Для этого вам необходимо подредактировать файл <tt>~/.cpan/CPAN/MyConfig.pm:</tt> указать в строке <tt>&makepl_arg& =& q[prefix=/home/you],</tt> и теперь всегда при автоматической инсталляции модули будут ставится к вам в $HOME/lib/perl5 Более подробно обо всем этом можно прочитать в документации к <tt>ExtUtils::MakeMaker.</tt> NB: Современный Модуль СPAN все сам спросит и сделает. <sect>Где взять документацию по перлу<label id="Где взять документацию по перлу"> <sect1>Какие есть книжки про Перл?<p> По perl, есть много хороших книжек, о которых я вкратце расскажу и еще больше плохих, о которых упоминать не стоит. В целом, за некоторыми исключениями правило просто -- если оригинал выпустило издательство O'Reilly, то эту книгу стоит читать :-) Рандал Шварц, Том Кристиансен. Изучаем перл (aka Llama book) (В оригинале Learning Perl By Randal Schwartz, Tom Christiansen & Larry Wall) Книга для начинающих, рассказано что такое perl, разобраны все основные конструкции языка, регулярные выражения, использование CPAN и написание CGI-программ. Llama book я рекомендую всем для знакомства с perl. Том Кристиансен, Натан Торкингтон, Perl: библиотека программиста (В оригинале Perl Cookbook By Tom Christiansen & Nathan Torkington) В этом толстом 700-страничном томе разобрана куча практических вопросов, возникающих при написании программ на perl: от преобразования регистров символов до написания TCP-серверов. Скотт Гулич, Шишир Гундаварам, Гюнтер Бирзнекс: CGI программирование на Perl (В оригинале CGI Programming with Perl, second edition by Scott Guelich, Shishir Gundavaram and Gunter Birznieks) Подробное руководство по написанию CGI-программ. Начиная с описания протокола HTTP и включая такие темы, как отправка почты, работа с базами данных, создание графических файлов из программ, XML, модули шаблонов и mod_perl. В perldoc perlbook описаны другие интересные англоязычные книги по perl. O'Reily в 2000 году выпустил The Perl CD Bookshelf, содержащий электронные версии большинства своих книг по perl и многие люди держат его на своих сайтах. Я URL давать не буду, они быстро становятся устаревшими, воспользуйтесь поисковыми системами. <sect1>Где взять стандартную документацию про такое-то свойство перла?<p> Перл приходит с полным набором документации и набором программ для перевода в разные форматы. Обычно для подробного ознакомления с некоторой особенностью перла пишут "perldoc perlсвойство" или "man perlсвойство". Базовый набор "свойств" таков: <itemize> <item> Основы perldata, perlvar, perlsyn, perlop, perlsub <item> Запуск perlrun, perldebug <item> Функции perlfunc <item> Objects perlref, perlmod, perlobj, perltie <item> Data Structures perlref, perllol, perldsc <item> Modules perlmod, perlmodlib, perlsub <item> Regexps perlre, perlfunc, perlop, perllocale <item> Moving to perl5 perltrap, perl <item> Linking w/C perlxstut, perlxs, perlcall, perlguts, perlembed <item> Various <url url="http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html" name="http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html"> (not a man-page but still useful) <item> perl О перле вообще <item> perldelta Что нового в последней версии перла <item> perlfaq FAQ <item> perltoc Подробное оглавление ко всей документации <item> perldata Типы данных <item> perlsyn Синтаксис языка <item> perlop Арифметические, логические, строковые операции и их приоритет <item> perlre Регулярные выражения (обработка текста и поиск) <item> perlrun Опции командной строки <item> perlfunc Встроенные функции <item> perlvar Специальные переменные <item> perlsub Как писать свои функции (процедуры) <item> perlmod Устройство и принцип работы модулей <item> perlmodlib Модули: создание собственных библиотек <item> perlmodinstall Поиск и установка модулей и библиотек на CPAN <item> perlform "Форматы", или шаблоны для выводимых данных <item> perllocale Поддержка интернационализации <item> perlref Ссылки и указатели на данные <item> perldsc Введение в структурные типы данных <item> perllol Структуры данных: массивы и списки <item> perltoot Введение в объектно-ориентированное программирование <item> perlobj Объекты в перле <item> perltie Связь объектов с обыкновенными переменными <item> perlbot Perl OO tricks and examples <item> perlipc Связь между процессами: pipes, sockets, сигналы и др. <item> perldebug Отладка программ <item> perldiag Сообщения об ошибках <item> perlsec Вопросы безопасности <item> perltrap Возможные грабли и ловушки <item> perlport Как писать портабельные программы <item> perlstyle Стиль программирования на перле <item> perlpod Формат стандартной документации и документация, встраиваемая в исходные тексты программ <item> perlbook О книгах про перл<p> --- (для совсем крутых) ---<p> <item> perlembed Способы внедрения перл-программ в программы на C/C++ <item> perlapio Собственный API, используемый в исходниках перла <item> perlxs XS -- программирование перловских библиотек, используемых вместе с библиотеками на C <item> perlxstut Учебник по XS <item> perlguts Внутренние функции перла для разработчиков <item> perlcall Соглашения о вызове перловских функций из C <item> perlhist История и полный список всех версий перла</itemize> <sect1>Я под виндами, man у меня нет, где брать стандартную документацию?<p> Перл для win32 поставляется с программой perldoc (пользоваться: "perldoc perlfunc" и т. д.) а также с документацией в HTML: если хочешь perldoc perlfunc, ищи perlfunc.html. <sect1>Где взять описание модуля Lib::Module?<p> Формат стандартной документации по перлу (pod) обладает возможностью встраивания прямо в тексты программ, а программа perldoc -- возможностью извлечения этой документации. Поэтому, чтобы получить документацию по модулю <tt>Math::Trig</tt>, просто запустите <tt>perldoc Math::Trig</tt> -- perldoc найдет этот модуль и покажет документацию из него. Если модуль не является частью какой-то библиотеки, а существует сам по себе, например, CGI.pm тогда просто perldoc CGI.pm или даже perldoc CGI. NB: программа perldoc не очень расторопна, поэтому при установке перла под UNIX производится создание manpages для всех модулей, так что man CGI или man Math::Trig покажет быстрее, чем perldoc. <sect1>Есть ли подробный большой FAQ по перлу?<p> Да, он тоже входит в набор стандартной документации. Отправная точка -- perldoc perlfaq, это оглавление, из которого можно узнать, что FAQ состоит из девяти больших частей. <sect1>Я крутой разработчик, где лежит описание того, как сделать perldoc/manpage к своей библиотеке?<p> Про это говорят в perldoc perlpod, в man pod2man.<p> <sect1>А есть ли где в интернете хорошие доки по перлу?<p> Да. На мой взгляд, серия статей Рандала Шварца для Unix Review Perl Columns -- лучшее введение в перл, и намного интереснее и полезнее книг Llama и Camel (<em>мнения авторов не всегда совпадают с мнением координатора -- Аммосов</em>). Почитать их можно на <url url="http://w3.stonehenge.com:80/merlyn/UnixReview/" name="http://w3.stonehenge.com:80/merlyn/UnixReview/">. Почему я считаю это лучшим введением в перл? Потому что это отдельные небольшие статьи, каждая из которых иллюстрирует конкретные возможности перла на примере написания программы для достаточно простой задачи. Все же статьи охватывают практически весь спектр возможностей перла -- от написания скрипта в одну строку, который может поменять Иванов на Сидоров во всех файлах в дереве директорий, до основ объектно-ориентированного программирования и принципов создания собственных модулей и библиотек. <sect1>А где можно найти русскую доку по перлу?<p> Если нет возможности купить в магазине, то есть кое-что и в Интернете: книга Маслова "Введение в перл", например. <url url="http://citforum.ru/koi/internet/perl_tut/" name="http://citforum.ru/koi/internet/perl_tut/"> Есть и другие, особо хороших не видел.<p> <sect>Как сделать на перле то-то (основные возможности языка)<label id="Как сделать на перле то-то (основные возможности языка)"><p> <sect1>Как пользоваться функцией pack (getpwuid, dmbopen)?<p> man perlfunc или "perldoc -f имя_функции | pod2text"<p> <sect1>Как узнать размер файла (дату создания, атрибуты, разрешения на доступ)?<p> С помощью так называемых файловых тестов, которые имеют вид (-тест "имя файла"), например: <descrip> <tag>(-s "file.txt")</tag>размер файла <tag>-e</tag>существует ли файл <tag>-w</tag>доступен ли на запись <tag>-M</tag>количество дней со дня модификации <tag>-t</tag>является ли терминалом </descrip> (Как обычно, это неполный список. См. man perlfunc) <sect1>Как написать модуль?<p> Простейший случай:<p> Сам модуль представляет из себя файл с именем <tt>MyModule.pm</tt> следующего содержания: <tscreen><verb>MyModule.pm package MyModule; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(идентификаторы для экспорта: подпрограммы, имена переменных и пр.); # Теперь после того, как вы напишите use MyModule в своей программе, у вас все # упомянутые здесь идентификаторы станут видны. Этот метод не рекомендуется, # так как вы "засоряете" именное пространство программы. # И/или @EXPORT_OK = qw(идентификаторы для экспорта: подпрограммы, имена переменных и пр.); # Идентификаторы, прописанные здесь будут видны в Вашей программе как # MyModule::Идентификатор (подпрограммы) или $MyModule::Идентификатор # (переменные) # Или, Вы можете написать use MyModule qw(Ид1 Ид2 $Ид3); и все будет как в # первом случае. Это рекомендуемый вариант. ... ваши подпрограммы и пр .... 1; # Обязательно </verb></tscreen> Программа, его использующая: <tscreen><verb>myprg.pl use MyModule qw(...); # Ну и далее </verb></tscreen> Подробности смотрите в perlmod(1), Exporter(3pm), Camel Book <sect1>Как получить доступ к n-му символу строки?<p> Надо использовать функцию substr. Например, получить 15й символ из строки $string: <verb>$char = substr($string, 15, 1);</verb> Установить с 15го символа строки $string и обрезать ее: <verb>substr($string, 15) = 'A';</verb> Можно даже использовать более чем односимвольные последовательности: <verb> $ss = substr($string, 15, 5); substr($string, 15) = 'Хо-Хо'; </verb> <sect>email<p> <sect1>Как отправить почту из скрипта на Perl?<p> <sect2>Простой способ для счастливых владельцев Unix<p> <tscreen><verb> open MAIL, '|/path/to/your/sendmail -t'; # Pipe к sendmail print MAIL "From: mailrobot@my.host\n", "To: webmaster@my.host\n", "Subject: Wow! I got some mail for you!!!\n\n", $message; close MAIL or die "Sendmail failed: $!"; </verb></tscreen> <sect2>Более гибкий способ -- Mail::Mailer<p> <tscreen><verb> #!/usr/bin/perl -w use strict; use Mail::Mailer; my $mailer = new Mail::Mailer; my %headers; $headers{"To"} = 'apavel'; $headers{"Subject"} = 'Привет'; my $body = "Привет!\nКак дела?"; $mailer->open(\%headers); print $mailer $body; $mailer->close; </verb></tscreen> <sect1>Как отправить почту с вложениями (attachments)?<p> <sect2>Используя MIME::Entity из комплекта MIME::Tools<p> <tscreen><verb> #!/usr/bin/perl -w use strict; use MIME::Entity; my $message = MIME::Entity->build( To => 'apavel', Subject => "Пошлые анекдоты и голые девки", Data => ["Пошлые анекдоты тут"], Charset => "koi8-r", Encoding=> "8bit", ); $message->attach( Path => "naked_girls.jpg", Type => "image/jpg", Encoding => "base64", Disposition=>"attachment", ); open SENDMAIL, "|/usr/sbin/sendmail -t" or die "sendmail: $!"; $message->print(\*SENDMAIL); close SENDMAIL or die "sendmail failed: $!"; </verb></tscreen> <sect1>Как определить правильность адреса электронной почты Internet?<p> Гарантированно -- никак. Вы можете проверить адрес на правильность синтаксиса при помощи Email::Valid. И все. Если нужен гарантированно правильный e-mail для web-сайта, вы можете воспользоваться методом от Алексея Тутубалина: требовать пароль для доступа к информации, а сам пароль высылать по электронной почте. Тогда человек должен будет указать свой e-mail, конечно же, если ему интересна эта информация с вашего сайта. <sect1>Как получать почту в Unix?<p> Самое простое - прописать ваш скрипт в .forward или /etc/aliases: .forward: <verb> | /path/to/your/script </verb> /etc/aliases <verb> pupkins: "|/path/to/yours/script" </verb> Более подробно смотрите в документации на ваш MTA. Скрипт должен читать из стандартного потока ввода. Например: <tscreen><verb> #!/usr/bin/perl open (F,">/tmp/mbox") or die $!; while (<STDIN>) { print F $_; } close (F); </verb></tscreen> <sect1>Как разобрать письмо на составные части?<p> <sect2>Ручным разбором<p> письмо состоит из двух частей - заголовка и тела. Заголовок состоит из пары: имя заголовка и значение заголовка, разделенные двоеточием ":", например: <tt/test/ (более подробно смотрите в rfc 822, 2822). Тело письма - просто текст. Пример скрипта для разбора письма. <tscreen><verb> #!/usr/bin/perl # Читаем письмо со стандартного потока ввода STDIN my (%head, @body); # %head - пары { header => value } # @body - тело письма { local $/ = ""; # читаем все до пустой строки - разделителя my $head = <STDIN>; # заголовка и тела письма $head =~ s|\n\s+| |gs; # если значение заголовка длинное, # то он переносится на следующую строку и # начинается с пробельного символа. соединяем строку my @head = split /\n/,$head; # делим по строкам заголовок; # разделяем строку вида Subject: test на пары "Subject" и "test", # и возвращаем их как "subject" => "test" %head = map { my ($a,$b) = split (/:\s+/,$_,2); lc $a => $b } @head; # теперь в %head собраны все заголовки и их значения # предупреждение: я не рассматриваю случай присутствия в заголовке # письма одинаковых заголовков (как то "Received:") - как их сделать - # ваша задача. } @body = <STDIN>; # теперь в body тело письма, и с ним можно работать. </verb></tscreen> Теперь можно смотреть на $head{'content-type'} и решать, что с ним делать. <sect2>Используя MIME::Parser<p> <tscreen><verb> #!/usr/bin/perl -w use strict; use MIME::Parser; # Построить объект MIME::Parser для разбора письма my $parser = new MIME::Parser; # Сохранять временные файлы в /tmp $parser->output_under("/tmp"); # Разобрать STDIN. my $entity = $parser->parse(\*STDIN); # Теперь $entity -- это объект MIME::Entity # Получить какой-нибудь заголовок письма можно у $entity->head (объект MIME::Head): my $subject = $entity->head->get('Subject'); # Если это письмо состоит из одной части, то ее можно получить так: my $body = $entity->body_as_string; # ... Но в 1996 netscape выкатила свой MUA, который по-дефолту отправлял одно # и тоже сообщение в HTML и text/plain и легкая жизнь для разработчиков # почтовых систем закончилась. Пришлось разбирать MIME. if ($entity->is_multipart) { for (my $i = 0; $i < $entity->parts(); $i++) { my $part = $entity->parts($i); # Теперь $part -- это объект MIME::Entity, содержащий одну из частей этого # этого письма. Обратите внимание, что $part в некоторых случаях # может тоже содержать другие части, например, для MIME типа message/rfc822, # так что в общем случае, на этом месте должна быть рекурсивная подпрограмма # разбора # Открыть эту часть для чтения my $IO = $part->bodyhandle->open("r") or die "open body: $!"; while (defined($_ = $IO->getline)) { # Делать чего-то с ней } $IO->close(); } } else { # Это сообщение из одной части: $entity->body_as_string; } # Удалить временные файлы $entity->purge(); </verb></tscreen> <sect1>Как декодировать строки типа =?koi8-r?B?UmU6IPfFzt?<p> Можно использовать MIME::Words, <tscreen><verb> #!/usr/bin/perl -w use MIME::Words qw(:all); use Convert::Cyrillic; use strict; my $string = 'Re: =?KOI8-R?B?0sHCz9TB ?= c MIME =?KOI8-R?B?zsE= ?= perl '; # Простой вариант: my $decoded = decode_mimewords($subject); # Но он не выдает информации о кодировке, так что лучше использовать # другую форму вызова: my $decoded = join("", map {xcode(${$_}[1], ${$_}[0])} decode_mimewords($string) print "decoded: $decoded\n"; # процедура перекодирования из чарсета, используемого в сообщении # в koi8 sub xcode { my ($charset, $src) = @_; my %charsets = ( 'windows-1251'=>'WIN', 'iso8859-5'=>'ISO', 'koi8-r'=>'KOI8', 'koi8r'=>'KOI8', 'koi8-u'=>'KOI8', 'utf-8'=>'UTF8', 'utf8'=>'UTF8' ); return $src unless ($charsets{lc($charset)}); Convert::Cyrillic::cstocs($charsets{lc($charset)}, 'KOI8', $src); } </verb></tscreen> <sect1>Как получить почту по POP3?<p> Используя Net::POP3. <tscreen><verb> #!/usr/bin/perl -w use strict; use Net::POP3; my $server = "oops"; my $pop = new Net::POP3($server); # Залогинится my $msgs = $pop->login("alladin", "opensesame"); # login возвращает undef, если не удалось залогинится и число сообщений # в почтовом ящике, если удалось. die "Login failed" if not defined $msgs; print "$msgs сообщений\n"; # Список сообщений. list возвращает ссылку на хэш, ключами которого являются # номера сообщений, а значениями -- размер письма в байтах. my %list = %{$pop->list}; for my $msg_num (keys(%list)) { print "#$msg_num - $list{$msg_num} байт\n"; # Получить сообщение. get вернет ссылку на массив строк my $msg = $pop->get($msg_num); # сделать с ним чего-нибудь open F, ">msg$msg_num" or die "msg$msg_num: $!"; print F join("", @$msg); close F; # Отметить сообщение для удаления. Оно будет удалено # при закрытии коннекта к серверу $pop->delete($msg_num); } # Закрыть соединение с сервером $pop->quit(); </verb></tscreen> <sect>CGI-скрипты<label id="CGI-скрипты"><p> <sect1>Что такое CGI и как с ним работать<p> CGI -- Common Gateway Interface. Стандарт интерфейса внешних программ с http-сервером. <sect2>Как работать<p> HTTP -- клиент-серверный протокол, следовательно со стороны CGI-программы, как серверного процесса, все взаимодействие выглядит следующим образом <enum> <item> Получение данных от клиента <item> Обработка данных <item> Выдача ответа клиенту. </enum> Пункты 1 и 3 я вкратце опишу здесь, а 2, надеюсь, сделаете сами :-). Начнем с п.3, как наиболее простого. <sect3>3. Выдача данных клиенту<p> Обычно клиенту выдают текст в формате HTML (ничто не мешает Вам отправить ему и картинку/видео/etc). Для того, чтобы сервер и клиент вас поняли, необходимо сказать, что вы выдаете, c помощью заголовка Content-Type: mime-type/mime-subtype. Обратите внимание на регистр и последовательность -- если вы скажите нечто типа Content_type, то сервер вас скорее всего не поймет. (Сообщение типа "500 Internal Server Error" будет симптомом). Пример: <tscreen><verb> print "Content-Type: text/html\n"; # Мы выдаем текст в формате HTML. Также можно: text/plain -- простой текст, в # браузере отобразится аналогично тексту, заключённому между тегами # <pre></pre>. image/gif -- Картинка, формат gif video/mpeg -- # mpeg-видео И целая куча других форматов, см. файл mime.types из apache print "\n"; # <-- еще одна пустая строка, обозначает конец вывода наших # заголовков. ВАЖНО! # Теперь мы можем написать свой текст клиенту print qq{ <html> <head> <title>Моя первая CGI программа</title> </head> <body> <h1>Моя первая CGI программа</h1> </body> </head> }; </verb></tscreen> <sect3>Прием данных от клиента<p> Взаимодействие с клиентом обеспечивается так: Он заполняет форму своими значениями, нажимает на кнопку "submit", броузер кодирует данные соответствующим образом и отправляет их серверу. <sect4>Определение форм<p> Производится тегами <form> и </form>. Тег определения форм имеет следующие атрибуты<p> <descrip> <tag>action</tag> скрипт на сервере, который будет запущен на сервере для обработки данных формы. <tag>method</tag> тип взаимодействия с сервером. Может иметь значения GET и POST. Плюс, еще некоторые специальные, которые вы можете посмотреть в rfc<p> </descrip> (Аттрибуты перечислены не все, как обычно) Формы не могут быть вложенными. Элементы ввода Определяются тегами <input>, <textarea> и <select> тег <input>: <descrip> <tag><input type=checkbox></tag> Элемент ввода "Опция" <tag><input type=hidden></tag> Элемент ввода, который не виден пользователю <tag><input type=file></tag> позволяет загрузить файл на сервер <tag><input type=image></tag>Изображение. Если по нему щелкнуть, это вызовет submit формы и серверу будут выданы две переменные вида name.x и name.y, где name -- то, что вы пропишете в name=... тега <input> <tag><input type=password></tag>Элемент ввода Пароль. Позволяет ввести строку, которая на экране отображается звездочками. Никаких методов защиты при передаче на сервер не применяется <tag><input type=radio></tag>Радиокнопки <tag><input type=reset></tag>Кнопка сброса значений формы на начальные <tag><input type=submit></tag>Кнопка отправки формы на сервер <tag><input type=text></tag>строка ввода </descrip> Все теги <input> имеют атрибут name -- Наименование элемента. Служит для идентификации при передаче на сервер, а также другие типа value, width, etc, название и назначение которых можно опять-таки посмотреть в учебнике. Тег <textarea> -- Поле многострочного ввода.<p> Тег <select> -- списочный выбор<p> Пример: <tscreen><verb> <form action=/cgi-bin/myscript.pl method=GET> Имя: <input type=string name=name><p> Пол: <input type=radio name=gender value=male>Мужской <input type=radio name=gender value=female>Женский <input type=submit><input type=reset> </form> </verb></tscreen> <sect3>Как это будет видно в моей программе?<p> Это определяется методом формы, GET или POST В случае GET сервер установит переменную окружения QUERY_STRING в виде name1=vaue1&name2=value2&..&nameN=valueN.<p> В случае POST аналогичная строка будет записана на стандартный ввод. Ее длину можно получить через переменную окружения CONTENT_LENGTH. <p> В обоих случаях данные будут закодированы по следующему алгоритму: <itemize> <item>Если ASCII код символа больше 32 и меньше 128, то он будет выдан без изменения. <item>если символ - пробел, то он заменится на "+" (плюсик, без кавычек) все остальное преобразуется в вид %xx, где xx -- шестнадцатеричный код символа. </itemize> <sect4>Декодирование на perl<p> <tscreen><verb> tr/+/ /; s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge; </verb></tscreen> (списано из CGI.pm)<p> На самом деле, можно не возится со всем этим, а использовать модуль CGI.pm (Имеется в поставке Perl 5.004 и более поздних. Если у вас perl версии 4 или 1, нужно срочно взять на CPAN новую версию perl) <p> <sect2>Самый большой и последний пример<p> Предположим мы делаем поисковую систему, тогда у нас должен быть HTML с формой и программа, которая будет выполнять поиск. <tscreen><verb>search.html <html> <head> <title>Поиск</title> </head> <<body> <h1>Чего искать?</h1> <form action=/cgi-bin/search.pl method=get> Строка для поиска: <input type=text name=string width=70><p> Искать в <input type=checkbox name=searchin value=internet>Интернет <input type=checkbox name=searchin value=intranet>Интранет <input type=checkbox name=searchin value=extranet>Экстранет<p> <input type=submit value=Давай!><input type=reset value="Нет, не надо"> </form> </body> </html> </verb></tscreen> Программа: <tscreen><verb> #!/usr/bin/perl -- поменяйте, как надо use CGI qw(:standard); print "Content-Type: text/html\n\n"; # Не забудьте про два "\n" $string = param("string"); @searchin = param("searchin"); # searchin это checkbox и его значения # возвращаются списком # Искать мы сегодня ничего не будем print "<html> <body> <h1>Мы сегодня ничего не ищем</h1> Но, если бы искали, то: <br> Искали бы строку <b>$string</b><br>\n"; print "В <b>" . join(" ", @searchin) . "</b>\n"; print " </body> </html> "; </verb></tscreen> Да, кстати, если вы собираетесь писать в файлы с помощью CGI-программ, не забывайте про file locking (См. главу 8 за подробностями). <p> <sect1>Как заставить браузеры не кешировать выдаваемый результат?<p> Выдавайте заголовки в таком виде: <verb> print "Content-Type: type/sub-type\n"; # Подставьте Ваш тип/подтип print "Pragma: no-cache\n"; # Для HTTP/1.0 клиентов print "Cache-Control: no-cache\n"; # Для HTTP/1.1 print "Expires: Thu Jan 1 00:00:00 1970\n\n"; # Это уже любой броузер должен # понять </verb> <sect1>Как перенаправить клиента на другой URL?<p> В заголовке напишите: <verb> print "Status: 302\n"; # Или 301. Разница состоит в том, что по стандарту 301 значит "перемещён # навсегда", а 302 -- "перемещён временно" print "Location: ВАШ Новый URL\n"; # URL должен быть указан абсолютный print "URI: ВАШ новый URL\n\n"; # Для http/1.0 </verb> Подробности: см. rfc1945(http/1.0), rfc2068(http/1.1) <sect1>Как загрузить файл на сервер?<p> В поставку CGI.pm входит пример file_upload.cgi. За подробностями смотрите CGI(3). Учтите, что если у вас система, которая разделяет бинарные и текстовые файлы -- вам понадобится использовать binmode. Также, помните, что "Русский Apache" не будет производить перекодировку multipart форм.<p> <sect1>Как получить файл по URL?<p> Возьмите на CPAN библиотеку libwww-perl и смотрите lwpcook(3), там написаны основы использования библиотеки и есть примеры.<p> <sect1>Как использовать плюшки (cookies)?<p> Плюшки работают следующим образом: программа CGI добавляет в свой ответ директиву Set-Cookie, а клиент (если он поддерживает плюшки), при следующих запросах возвращает их в заголовке Cookie. Сами плюшки представляют из себя пары имя=значение. <sect2>Синтаксис Set-Cookie<p> <verb>Set-Cookie: имя=значение; expires=дата; path=путь; domain=домен; secure</verb> <p> Сервер может установить не более 20 плюшек, размер всей плюшки не может превышать 4Kb.<p> <descrip> <tag>Имя=значение</tag> единственный обязательный параметр. И имя, и значение могут быть строками текста, не содержащими символов перевода строки, пробелов, ; и табуляции. Если необходимо их использовать, можно эти символы закодировать при помощи стандартного uri-кодирования.<p> <tag>expires=дата</tag> устанавливается дата истечения срока действия плюшки. После этой дата плюшка клиентом не возвращается. Если дата не установлена, то плюшка действует до завершения работы браузера. <tag>path=путь</tag> обозначает путь на сервере, для которого возвращается эта плюшка, если не указано, то только для того, который ее создал. <tag>domain=домен</tag> домен, для которого возвращается эта плюшка. Может быть доменом уровня 2 и выше, т.е. .example.ru, а не .ru. Если не установлен, то плюшка возвращается только серверу, который выдал плюшку. <tag>secure</tag> указывает, что плюшка должна возвращаться только по защищенному соединению (SSL). </descrip> <sect2>Синтаксис ответа клиента<p> <verb>Cookie: имя1=значение1; имя2=значение2;....;имяn=значениеn</verb> Программы CGI могут получить пары имя-значение через переменную окружения HTTP_COOKIE. В модуле CGI.pm значение можно получить при помощи метода cookie(). В деталях, механизм плюшек описан в rfc2109 (HTTP State Management Mechanism. D. Kristol, L. Montulli. February 1997.) Пример: 1. Установка плюшки <tscreen><verb>setcookie.pl #!/usr/bin/perl print "Content-Type: text/html\n"; print "Set-Cookie: mycookie=some+string\n\n"; print "<html><head><title>Установка плюшки</title></head>\n"; print "<body><h1>Установка плюшки</h1>\n"; print "<a href=getcookie.pl>Щелкни здесь,</a> чтобы посмотреть, что получилось. print "</body></html>"; </verb></tscreen> 2. Получение плюшки <tscreen><verb>getcookie.pl; #!/usr/bin/perl use CGI qw(:standard); print "Content-Type: text/html\n\n"; print "<html><head><title>Получение плюшки</title></head>\n"; print "<body><h1>Плюшка</h1>\n"; print "mycookie = ", cookie('mycookie'); print "</body></html>"; </verb></tscreen> <sect>Web серверы<label id="Web серверы"><p> <sect1>Как заставить WWW сервер исполнять CGI-программы?<p> <sect2>Apache для Unix/Win32<p> Надо отредактировать конфигурационные файлы (я рассчитываю, что у вас default конфиги apache) <em>(NB: Apache/1.3.6 и выше по умолчанию конфигурируется только файлом httpd.conf. Директивы все остались прежние, просто их слили в один файл)</em> <sect3>Способ 1<p> srm.conf Директива <tt>ScriptAlias</tt> <tscreen><verb> ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/ </verb></tscreen> и в файле access.conf прописать<p> <tscreen><verb> <Directory /usr/local/apache/cgi-bin/> Options ExecCGI </Directory> </verb></tscreen> (если пригляделся, там нужно только раскоментировать опции) Это позволит вам помещать программы в каталог /usr/local/apache/cgi-bin/ и они будут видны по URL http://you/cgi-bin/program_name <sect3>Способ 2<p> Прописать <tscreen><verb> <Files *.cgi> SetHandler cgi-script Options ExecCGI </Files> </verb></tscreen> и apache буде исполнять файлы с расширением .cgi из любого каталога как CGI-скрипты. <sect1>Мой CGI-скрипт не работает! Как установить причину?<p> Прежде чем читать дальше, убедитесь в том, что: <itemize> <item>Вы запустили скрипт с командной строки (не через CGI!!!) и он работает, а не говорит "compilation stopped due to errors" <item>Скрипт не только работает, но и выдает нужный заголовок с пустой строкой после него <item>Для многопользовательских систем) Вы сделали тоже под пользователем, под которым работает сервер (su -m имя_пользователя -c script в Unix) и он столь же благополучно работает </itemize><p> <sect1>Ошибки HTTP 500 и 403. Что это значит и что с ними делать<p> error_log для того и служит, чтобы туда смотреть :-) <sect2>Ошибка 403 "Forbidden"<p> Возникает, если сервер не может отдать вам содержимое по причине отсутствия полномочий. Проверьте: <itemize> <item> Может ли user (для apache под Unix это обычно nobody), под которым работает, сервер исполнять файл? <item> В частности, может ли сервер исполнять интерпретатор (в случае скриптовых языков)? <item> Можно ли для этого каталога исполнять программы CGI? (Options ExecCGI для apache) </itemize> <sect2>Ошибка 500 "Internal Server Error"<p> Возникает в случае внутренней ошибки. Проверьте: <itemize> <item>Выводите ли вы строку "Content-Type:"? <item>Правильно ли вы ее выводите? (Content-type, Content_Type -- неправильно) <item>Заканчиваете ли вы хедеры пустой строкой? Нельзя написать <tt> print "Content-Type: text/html\n"; print "<HTML>\n"; </tt> Надо писать: <tt> print "Content-Type: text/html\n\n"; # Два "\n" print "<HTML>\n"; </tt> </itemize> Также, эта ошибка возникает, если CGI-программа завершилась с ненулевым кодом возврата, что часто встречается в случае некомпилируемости вашего скрипта perl'ом.<p> Совет: делайте <tt>use CGI::Carp qw(fatalsToBrowser)</tt> во время отладки, тогда вам выдадут сообщение об ошибке perl.<p> <sect1>Почему $ENV{REMOTE_HOST} пуста?<p> apache устанавливает переменную окружения REMOTE_HOST, если ему разрешено проводить dns запросы для определения имени. Для 1.3.x по-умолчанию это выключено. Включается/выключается директивой HostNameLookups, которая может принимать 3 значения: on -- проводить запросы, off -- не запрашивать dns и double -- делать двойные запросы: запрашивать имя хоста, а затем по имени запрашивать ip адрес, для безопасности.<p> <sect1>Почему $ENV{REMOTE_USER} пуста?<p> Переменная окружения REMOTE_USER устанавливается apache в случае, если скрипт защищен паролем. Как это сделать рассказано на <url url="http://www.apacheweek.com/features/userauth" name="http://www.apacheweek.com/features/userauth"> и <url url="http://www.apacheweek.com/features/dbmauth" name="http://www.apacheweek.com/features/dbmauth"><p> <sect1>Как получить пароль при http-авторизации?<p> Прописать <tscreen><verb> RewriteEngine On RewriteCond %{HTTP:Authorization} ^(.*)$ [NC] RewriteRule /.* - [E=HTTP_AUTHORIZATION:%1] </verb></tscreen> в конфиг апача (Должен быть включен mod_rewite) и apache будет выставлять в окружение переменную $ENV{HTTP_AUTHORIZATION}. <p> Пример как ее декодировать: <tscreen><verb> #!/usr/bin/perl -w use strict; use MIME::Base64; print "Content-Type: text/plain; charset=koi8-r\n\n"; # В $ENV{HTTP_AUTHORIZATION} сейчас нечто типа: # Basic YXBhdmVsOmZvb2Jhcg== my ($type, $encoded) = split(/ /, $ENV{HTTP_AUTHORIZATION}); my ($login, $passw) = split(/:/, decode_base64($encoded)); print "Type: $type Login: $login Password: $passw "; </verb></tscreen> Учтите, что на некоторых системах, чужие юзеры могут заглядывать в environment процесса и могут таким образом узнать пароли. <sect>Ускоряемся<label id="Ускоряемся"><p> <sect1>Моя CGI программа работает слишком медленно/создает большую нагрузку на сервер/etc. Что делать?<p> <itemize> <item> Вкомплировать интерпретатор в сервер (mod_dtcl, mod_perl, pyapache для apache, серверы с поддержкой servlets, AOL server с его поддержкой tcl). <item> Переделать на FastCGI или аналогичную технологию. <item> Переписать ее на компилируемый язык, если написано на скриптовом. Тем самым вы избавитесь от оверхедов интерпретатора. <item> Взять более мощный сервер (IBM RS/6k F50, к примеру :-) </itemize> <p> <sect1>Что такое mod_perl?<p> Это perl, вкомпилированный в apache, что придает многие преимущества: <itemize> <item>Скрипты и модули компилируются один раз, что значительно увеличивает скорость ответа на запрос HTTP (до 2000%); <item>Можно написать свои обработчики различных стадий запроса; <item>Кеширование соединений с сервером БД -- еще прибавка к скорости; <item>Директива SSI #perl позволяет в server-parsed html вызывать функции perl; <item>секции <perl></perl> в конфиге apache, для его конфигурирования; </itemize> Правда, ничего не дается даром и самый важный недостаток mod_perl -- <em>огромный</em> расход памяти: если обычный apache занимает при работе обычно меньше мегабайта, то apache с mod_perl размером в 10-15 Mb -- вполне нормальное явление. Но, при правильной настройке, значительная часть этой памяти будет shared между копиями процессов. Основной сайт про mod_perl - <url url="http://perl.apache.org" name="http://perl.apache.org">, в частности великолепный mod_perl Guide на <url url="http://perl.apache.org/guide/" name="http://perl.apache.org/guide/">. Новости mod_perl на сайте <url url="http://www.take23.org" name="Take23.org"> <p> <sect1>В чем разница между скриптом для CGI и mod_perl?<p> Программа CGI исполняется один раз, за тем умирает. Под mod_perl же она загружается и исполняется до смерти сервера (в случае использования Apache::Registry, см. далее), поэтому она требует более аккуратного описания - закрывать файлы, не использовать много памяти, и тд. Более подробно о переписывании программ CGI под mod_perl - <url url="http://perl.apache.org/guide/porting.html" name="http://perl.apache.org/guide/porting.html">. <sect1>В чем разница между исполнением под Apache::Registry и Apache::PerlRun?<p><p> <sect1>Как использовать постоянное соединение с БД под mod_perl?<p> <sect1>Что такое FastCGI?<p> <sect>Использование других языков для написания CGI-программ<label id="Использование других языков для написания CGI-программ"><p> <sect1>Можно ли писать CGI-программы не на Perl?<p> Да, можно. CGI не зависит от используемого языка программирования. <sect1>Какие существуют библиотеки на других языках для CGI?<p> Это далеко неполный список. (Почти полностью списано из CGI_metaFAQ от brian d foy). <sect2>C/C++<p> <itemize> <item><url url="http://www.boutell.com/cgic/" name="Tom Boutell's cgic"> (C library) <URL:http://www.boutell.com/cgic/> <item><url url="http://www.eekim.com/software/cgihtml/" name="Eugene Eric Kim's cgihtml"> (C library) <URL:http://www.eekim.com/software/cgihtml/> <item><url url="http://www.camtech.com.au/jemtek/cgi/itemb/" name="JemTek's (C Library)"> <URL:http://www.camtech.com.au/jemtek/cgi/itemb/> <item><url url="http://www3.pair.com/webthing/cgiplusplus/" name="Nick Kew's CGI++ (C++ library)"> <URL:http://www3.pair.com/webthing/cgiplusplus/> <item><url url="http://www.cs.hmc.edu/~sbooth/cgicc/" name="Stephen F. Booth's Cgicc (C++ library)"> <URL:http://www.cs.hmc.edu/~sbooth/cgicc/> <item><url url="http://www1.dpe.net/C++/" name="S. Martin's CGI.h C++ library"> <http://www1.dpe.net/C++/> </itemize><p> <sect2>Haskell<p> <itemize> <item><url url="http://www.pms.informatik.uni-muenchen.de/mitarbeiter/panne/haskell_itembs/CGI.html" name="http://www.pms.informatik.uni-muenchen.de/mitarbeiter/panne/haskell_itembs/CGI.html"> <item><url url="http://www.cse.ogi.edu/~erik/Personal/cgi.htm" name="http://www.cse.ogi.edu/~erik/Personal/cgi.htm"> </itemize> <em>По поводу Haskell <url url="http://wave.philol.msu.ru/fp/haskell/" name="http://wave.philol.msu.ru/fp/haskell/"></em> <sect2>Phyton<p> <sect2>rexx<p> <sect2>Ada<p> <sect2>Java<p> <itemize> <item>Java CGI package <url url="ftp://ftp.orbits.com/pub/software/" name="ftp://ftp.orbits.com/pub/software/">. Лучше использовать сервлеты: <itemize> <item><url url="http://www.servletscentral.org" name="http://www.servletscentral.org"> <item><url url="http://javasoft.com/products/servlet/" name="http://javasoft.com/products/servlet"> <item><url url="http://java.apache.org" name="http://java.apache.org"> </itemize> </itemize> <sect>БД на плоских файлах<label id="БД на плоских файлах"><p> <sect1>Я хочу какую-нибудь простейшую БД и прямо сейчас!<p> Вы можете использовать простой текстовый файл с разделителями. Например, если мы пишем нечто типа телефонной книги, то вполне вероятно предположить, что ни в чьем имени, ни в номере телефона не встретится последовательность ::, так что именно ее и можно использовать в качестве разделителей. Файл с данными может выглядеть так: <tscreen><verb>phones.data Иванов И.И.::888-0000::Какая-то улица, 17, кв 40 Сидоров П.И.::888-8429::Другая улица, 5, кв 21 ...... и тд. </verb></tscreen> тогда программа, которая читает данные, может быть примерно такого вида: <tscreen><verb>dump_phones.pl #!/usr/bin/perl $filename = 'phones.data'; # открываем файл open DATA, $filename or die "Невозможно открыть $filename: $!"; # читаем построчно из файла while (<DATA>) { chomp; # удаление символа конца строки # теперь в $_ есть строка и мы ее разделяем на переменные ($name, $phone, $address) = split(/::/); # и выведем на печать print "Имя: $name, телефон: $phone, адрес: $address\n"; } close DATA; </verb></tscreen> Больше проблем возникает в случае, если надо удалить или отредактировать запись, но и их можно довольно просто и элегантно решить, если использовать механизм редактирования на месте (inplace edit) -- при использовании операции "ромб"(<>), можно читать из одного файла, а писать в другой: <tscreen><verb>change_phones.pl #!/usr/bin/perl $^I = '~'; # запускаем inplace edit while (<>) { # Обратите внимание, что мы не открывали файл: при такой #конструкции имя файла берется из командной строки chomp; ($name, $phone, $address) = split(/::/); if (.... некоторое условие, при котором мы оставляем наши данные ... ) { print "$name::$phone::$address\n"; # теперь данные есть в новом файле } } </verb></tscreen> если запустить это программу как <verb>change_phones.pl phones.data,</verb> то в текущем каталоге будут два файла: phones.data, с записями, которые удовлетворили нашим условиям и phones.data˜ -- предыдущая копия. Также, во многих случаях, всю программу такого типа можно записать как one-liner: <verb>perl -i~ -n -e 'print if(... условие)'</verb> <sect2>Файлы CSV<p> Схема хранения данных, использованная в предыдущем примере весьма удобна, но страдает одним недостатком -- в значениях не может использоватся разделитель, или его приходится специальным образом обрабатывать при чтении и записи таких файлов. Text::CSV именно это и делает, позволяя оперировать файлами в формате CSV (comma-separated values). Например, записывать: <tscreen><verb> #!/usr/bin/perl -w use strict; use Text::CSV_XS; my ($name, $phone, $address) = ('ТОО "Рога и Копыта"', '888-00-11', 'Какая-то улица, 17'); my $csv = Text::CSV_XS->new({ # надо для русских букв 'binary'=>1 }); $csv->combine($name, $phone, $address) or die "Combine failed"; print $csv->string(); # Выведет "ТОО ""Рога и Копыта""",888-0000,"Какая-то улица, 17" </verb></tscreen> и читать <tscreen><verb> #!/usr/bin/perl -w use strict; use Text::CSV_XS; my $csv = Text::CSV_XS->new({ 'binary'=>1 }); open F, "<phones.csv" or die "phones.csv: $!"; my $line_num = 1; # Читаем по одной строке из файла while (my $line = <F>) { # Пробуем разобрать на поля. parse() возвращает 1, если все прошло ok # и 0, если строка ему не понравилась. if ($csv->parse($line)) { # fields возвращает значения my ($name, $phone, $address) = $csv->fields(); printf "%-30s %-15s %-30s\n", $name, $phone, $address; } else { # error_input вернет неправильную строку. die "Неправильная строка #$line_num: ", $csv->error_input(); } $line_num++; } close F; </verb></tscreen> <tscreen><verb>phones.csv "ТОО ""Рога и Копыта""",888-0000,"Широкий бульвар, 4" "Петров А.А.",887-00-00,"Длинный проспект, 124" </verb></tscreen> <sect2>Двоичные файлы<p> Для чтения двоичных файлов в Perl можно использовать функции read и unpack. К примеру, если использовать двоичный файл для хранения телефонной книги такого формата:<p> 40 символов -- фамилия, И.О. <p> 10 символов -- номер телефона, <p> 60 символов -- адрес,<p> то строка описания формата для unpack будет выглядеть так: <verb>$format_str = 'A40 A10 A60'</verb>, а сама программа, аналогичная первому примеру: <tscreen><verb>binary_phones.pl #!/usr/bin/perl $format_str = 'A40 A10 A60'; open DATA, 'binary.dat' or die "$!"; while (read(DATA, $buf, 40+10+60)) { # <DATA> не покатит: такая # конструкция будет читать до символа перевода строки, а это не то, что нужно ($name, $phone, $address) = unpack($format_str, $buf); # Теперь в $name, $phone, $address есть данные и с ними можно делать # все, что захочется } close DATA; </verb></tscreen> Чтобы вывести в файл такую запись можно использовать конструкцию типа <p> <tt> print FILE pack($format_str, $name, $phone, $address); </tt> <sect1>Можно ли как-нибудь из Perl получить доступ к dbf файлам?<p> Да, можно. На <url url="http://www.fi.muni.cz/~adelton/" name="http://www.fi.muni.cz/~adelton/"> есть модуль XBase, который позволяет читать/писать dbf. При чтении он даже поддерживает индексы. Кроме того, в комплект поставки также входит модуль DBD::XBase, при помощи которого можно оперировать dbf на SQL (более подробно про DBI -- далее). <sect1>А к MS access .mdb?<p> К файлам MS Access нельзя обращаться из perl напрямую. К MS Access можно обращаться по ODBC, при помощи DBD::ODBC. <sect1>Зачем и как нужно запирать (lock) файлы?<p> Представьте себе ситуацию когда одновременно работают несколько копий одной и той же программы (к примеру, cgi-скрипты, обслуживающие запросы), читающие/пишущие в один файл, тогда рано или поздно возникнет ситуация при которой один скрипт прочитал данные, произвел над ними некоторые действия и собрался записать их назад в файл, но в это же время другой скрипт тоже прочитал данные, тоже произвел над ними действия, но (!) он прочитал старые данные, которые он и запишет поверх данных, выданных другим скриптом. Таким образом, в файле останутся данные записанные одним из скриптов -- в лучшем случае, в худшем -- структура файла будет испорчена. Чтобы этого избежать в Unix и большинстве других ОС есть системный вызов flock(2) или аналогичный. <sect2>Как использовать flock<p> К примеру, скрипт который записывает имена вызывающих хостов в файл. (На деле такой список, конечно, можно получить из журнала регистрации web-сервера). <tscreen><verb>lock_exm.pl #!/usr/bin/perl use Fcntl; # Импорт констант open (HOSTS, '>>hosts.log'); # Файл открыт для добавления записи flock(HOSTS, LOCK_EX); # Теперь файл заблокирован: Если любой другой скрипт тоже вызовет flock на # этом файле, его flock не вернет управление в программу, пока мы не # разблокируем файл. Обратите внимание: flock -- декларативная функция, если # один из скриптов ее не использует при записи, то вся ваша блокировка не # работает. print HOSTS $ENV{REMOTE_HOST}, "\n"; # записали строку close HOSTS; # Файл при закрытии разблокируется автоматически -- unlock вызывать не надо. # Вывести сообщение для пользователей print "Content-Type: text/plain\n\n"; print "Название вашего хоста записано\n"; </verb></tscreen> Более подробный рассказ о flock и пример доступны на <url url="http://w3.stonehenge.com/merlyn/WebTechniques/col04.html" name="http://w3.stonehenge.com/merlyn/WebTechniques/col04.html"> <sect1>Чего делать на системах где flock() нет?<p> Судя по perlfaq5(1), можно использовать модуль File::Lock с CPAN. <sect>Реляционные, пост-реляционные и объектно-реляционные СУБД<label id="Реляционные, пост-реляционные и объектно-реляционные СУБД"><p> <sect1>Как использовать DBI?<p> <sect2>Введение<p> DBI -- это интерфейс прикладных программ к СУБД, использующим SQL в качестве языка запросов. Сам DBI определяет только набор функций, переменных и соглашений. Вся непосредственная работа выполняется Database Drivers (DBD) -- модулями, обеспечивающими связь с СУБД. DBI только обеспечивает стандартный интерфейс для этих драйверов. Полная схема архитектуры при работе DBI выглядит примерно так: <tscreen><verb> +----------------------+ | Прикладная программа | +----------------------+ | DBI | +----------------------+ | DBD | +----------------------+ | СУБД | +----------------------+ </verb></tscreen> <sect2>Подсоединение к СУБД<p> обеспечивается при помощи метода connect класса DBI: <verb>$dbh = DBI->connect($dsn, $user, $auth, {options});</verb> <descrip> <tag>$dbh</tag>Это объект, при помощи его методов осуществляются взаимодействия с СУБД. <tag>$dsn</tag>Строка, определяющая к какой базе данных подсоединятся и другие параметры. Зависит от DBD. На сегодняшний момент стандарта нет, но рекомендовано использовать стиль ODBC: <verb>dbi:<имя DBD>:databasename=<название БД>;host=<Имя хоста>;port=<порт></verb> <tag>$user</tag>Имя пользователя. <tag>$auth</tag>Нечто, авторизующее пользователя. Обычно пароль. <tag>options</tag>Параметры DBI, передаются через анонимный хеш. В настоящее время понимаются три параметра: <descrip> <tag>RaiseError</tag>если установлен, то при любой ошибке DBI убивает программу <tag>PrintError</tag>если установлен, то при ошибке DBI вызывает warn <tag>AutoCommit</tag>определяет порядок работы с транзакциями. </descrip> </descrip> Например: <verb>$dbh = DBI->connect('dbi:Pg:dbname=apavel', 'apavel', 'SomeSecret', {RaiseError=>1, AutoCommit=>0});</verb> Означает: Подсоедение к СУБД PostgreSQL, к базе данных apavel, с именем пользователя apavel и паролем. Все ошибки будут вызывать die, что удобно при отладке, а все изменения будут внесены только при подтверждении (commit) транзакций.<p> Отсоединение обеспечивается при помощи метода disconnect: $dbh->disconnect(); <sect2>Механизм курсоров и подготовки запросов<p> При работе с базами данных при помощи DBI используются курсоры -- специальные объекты, обеспечивающие последовательный доступ к результатам запросов. (В простейших случаях можно обойтись и без них, я расскажу об этом дальше.) Пример таблицы, используемый в дальнейшем: <tscreen><verb>foo.sql create table foo ( bar varchar(50), baz int ) </verb></tscreen> <sect3>Получение данных<p> <tscreen><verb> $cursor = $dbh->prepare('select bar, baz from foo'); # теперь $cursor -- курсор, и его необходимо исполнить $cursor->execute; # После исполнения запроса, результат можно получить из курсора при помощи # метода fetchrow_array while (($bar, $baz) = $cursor->fetchrow_array) { print "bar is: $bar, baz: $baz\n"; } </verb></tscreen> <sect3>Placeholders<p> Очень часто бывает надо подготовить какой-либо запрос, а потом использовать его с разными значениями данных. DBI предлагает для механизм placeholders: В запросе на месте таких данных указываются вопросительные знаки, а сами значения передаются в метод execute() курсора. Например: <verb>$cursor = $dbh->prepare('select bar from foo where baz=?'); $cursor->execute($baz);</verb> Особенно удобно это в случае вставки данных: <verb>$cursor = $dbh->prepare('insert into foo(bar, baz) values(?, ?)'); while ( ... ) { $cursor->execute($bar, $baz); }</verb> Таким образом, СУБД разбирает запрос только один раз, а затем просто исполняет его, что экономит время. (Естественно, это верно только для DBMS с раздельными parse и execute, сейчас ни MySQL, ни PostgreSQL такое не поддерживают, поэтому их реализации DBD просто сохраняют запрос переданный $dbh->prepare() и затем подставляют в него данные при каждом $sth->execute().) <sect2>Работа без курсоров<p> DBI предоставляет несколько методов для такого рода работы: Методы для запросов:<descrip> <tag>selectrow_array</tag> Возвращает одну строку запроса в виде массива <tag>selectall_arrayref</tag>Возвращает весь ответ сервера в виде массива ссылок на массивы. </descrip> Методы для выражений, не возвращающих значений <descrip> <tag>do</tag>исполняет запрос </descrip> Пример: <tscreen><verb> #получить значение bar при baz=3 ($bar) = $dbh->selectrow_array('select bar from foo where baz=3'); # установить baz в некоторое значение при bar='somestring' $dbh->do("update set baz=1 where bar='somestring'"); </verb></tscreen> <p> <sect1>Как работать с записями кусочками: первые N записей, следующие N...?<p> Можно несколькими способами: 1. Просто прокручивая курсор: <tscreen><verb> $c = $dbh->prepare('select baz, bar from foo'); $c->execute; # если нужна последовательность с 26 по 50 for ($k = 0; $k < 26; $k++) { $c->fetchrow_array; } # теперь можно вывести данные print "<table border=1><tr><th>bar</th><th>baz</th></tr>\n"; while (($bar, $baz) = $c->fetchtrow_array) { print "<tr><td>$bar</td><td>$baz</td></tr>\n"; } $c->finish; # Закрыть курсор print "</table>"; </verb></tscreen> 2. Используя курсоры СУБД <tscreen><verb> # Показан синтаксис PostgeSQL $dbh->do('declare mycursor cursor for select bar, baz from foo'); $dbh->do('move 25'); # И теперь будем получать данные $c = $dbh->prepare('fetch forward 25 in mycursor'); while (($bar, $baz) = $c->fetchrow_array) { print ....; } $c->finish; $dbh->do('close mycursor'); </verb></tscreen> 3. Для MySQL можно использовать директиву LIMIT <tscreen><verb> $c = $dbh->prepare('select bar, baz from foo limit 26,25'); while (($bar, $baz) = $c->fetchrow_array) { print ....; } $c->finish; </verb></tscreen> <sect1>Где взять документацию/учебник по SQL?<p> Мартин Грабер, "Введение в SQL", тем более, что недавно вышло новое издание на русском языке. <sect1>Как можно подсоединится к MS SQL Server?<p> Вроде как можно при помощи DBD::FreeTDS <sect>Где взять perl для m$ windows?<label id="Где взять perl для m$ windows?"><p> <sect>Борьба с глюками ms windows<label id="Борьба с глюками ms windows"><p> <sect1>Почему мой perl ругается на flock()?<p> Потому что на windows 9x нет flock(2). Используйте File::Lock с CPAN. <sect1>Тоже самое, но с fork()?<p> perl, входящий в <url url="http://sources.redhat.com/cygwin/" name="Cygwin"> -- набор утилит unix для windows, умеет эмуляцию fork. <sect1>Как бы мне добится нормальной сортировки в алфавитном порядке, преобразования в прописные/строчные буквы?<p> Perl для этого опирается на механизм locale. К счастью, это работает и на windows: Если надо работать с CP866, пишем <verb> use locale; use POSIX; &POSIX::setlocale(&POSIX::LC_ALL, "Russian_Russia.866"); print uc "Да, здесь будут заглавные буквы"; </verb> если KOI8 (sic!): <verb> &POSIX::setlocale(&POSIX::LC_ALL, "Russian_Russia.20866"); </verb> если с CP1251 -- просто <verb>use locale;</verb> Со всеми тремя кодировками работают и uc/lc, и /\w/ Проверено под NT на perl 5.005_02, собранном через VC++ 5.0 из исходников, скачанных с CPAN/ports/win32/Standard/ <sect1>Почему скрипты, которые я написал дома на windows, при загрузке на unix машину не работают?<p> Этому может быть миллион разных причин, но самая часто встречающаяся -- unix и наследники CP/M используют разные последовательности конца строки. Если вы их загружаете по ftp, включите режим ASCII. <sect1>Я включил на windows локаль в своих сриптах, но на Unix-сервере она не работает<p> Названия локалей на windows и unix системах различны. Локаль KOI8-R на unix скорее всего будет называтся ru_RU.KOI8-R, CP1251 может называтся по разному, спросите у администратора сервера. <sect>Прочее, не относящие к perl и web<label id="прочее, не относящие к perl и web"><p> <sect1>Как сделать чтоб моя программа автоматически запускалась в заданное время?<p> <sect2>Unix<p> Используйте стандартный демон <tt>cron.</tt> см. crontab(1) <sect2>Прочие платформы<p> Смотрите документацию на свои ОС. <sect>Авторы и копирайт<label id="Авторы и копирайт"><p> <sect1>Авторы ответов<p> <itemize> <item>Павел Аммосов <item>Артур Пенттинен <item>Михаил Поляков <item>Григорий Строкин </itemize> <sect1>Копирайт<p> Этот текст © группа авторов. Разрешается его распространение электронным или печатным образом, частично или полностью, если: сохранены все сообщения об авторстве, текст не изменялся (за исключением форматирования) и предпринимались достаточные попытки использовать наиболее современную версию. </article>