Перевод Anton Petrusevich <casus@mail.ru> и Alex Ott <ott@phtd.tpu.edu.ru>
Perl быстро стал ключевым инструментом в багаже типичного системного администратора и системного программиста.
Тем не менее, легко испугаться при виде 211 страниц печатной документации, которые идут с последней версией Perl (версия 5). У вас могут возникнуть вопросы: ``С чего начать?'' и ``Сколько из этого надо знать, чтобы писать на Perl?''
Один из самых простых способов научиться --- смотреть, как кто-нибудь другой решает элементарную задачу. Например, возьмем типичную задачу администрирования --- присвоение уникального номера (userID) новому пользователю. Для этого, мы должны узнать текущий максимальный номер пользователя(userID), и затем выбрать следующий максимальный номер.
Мы разобьем задачу на мелкие подзадачи и будем их решать.
Сначала рассмотрим печать первой колонки данных, выдаваемых командой
who
who | perl -ne '@F = split; print ''$F[0]\n'';'
Выходной поток who
становится входным потоком для Perl. Ключ -n
заставляет Perl исполнять некоторый код строчка за строчкой. Каждая входная
строчка помещается в переменную $_
. Ключ -e указывает код, который
будет исполнять Perl. Ключи можно комбинировать, как это сделали мы.
В нашем случае есть два Perl выражения: операция split
, и
print
. Операция split
разбивает содержимое $_
,
создавая список слов (считаем пробелы разделителями слов). Список
помещается в массив @F
. Затем операция print
печатает
содержимое первого элемента массива @F
и сразу перевод строки
(\n). Заметим, что первый элемент массива имеет номер 0, подобно массивам в
Си.
Можно немного урезать код, удалив split
:
who | perl -ane 'print ''$F[0]\n'' '
Заметим добавление ключа -a, заставляющего Perl автоматически выполнить
split
и поместить результат в массив @F
.
Чтобы укоротить строчку еще больше, добавим ещё ключ -l, который делает две вещи сразу:
$_
перед тем, как наш
код просмотрит строку.
В результате получаем:
who | perl -lane 'print $F[0]'
Чтобы еще сократить код, заменим ключ -n на -p, который заставляет
Perl печатать содержимое, оставшееся в $_
:
who | perl -lape '$_ = $F[0]'
Аналогичный по функциональности Perl-скрипт выглядит так:
#!/usr/bin/perl $\ = $/; # from -l while (<>) { # from -p chop; # from -l @F = split; # from -a $_ = $F[0]; # argument to -e print; # from -p }
Здесь видно, какое количество текста сократили нам ключи в командной строке Perl.
Переменная $\
задаёт терминатор-суффикс для каждой
операции print
, подобно переменной ORS в Awk. По умолчанию эта
переменная пуста, что позволяет вам самостоятельно контролировать формат выводимой
print
информации.
Здесь мы устанавливаем переменную $\
равной значению переменной
$/
, которая является разделителем полей ввода (подобно RS в
Awk). По умолчанию, её значение равно '\n'. Таким образом, мы
добиваемся перевода на новую строчку после каждой выведенной строки.
Теперь от команды who
перейдём к реальной задаче: разбор
файла паролей для поиска наибольшего номера пользователя (userID).
Файл паролей отличается от того, что выдаёт who
тем, что колонки
разделены не пробелами, а двоеточиями. Нет проблем --- сообщим об этом
Perl:
perl -F: -lane 'print $F[0]' /etc/passwd
С помощью ключа -F мы задали новый разделитель колонок и в результате получили список всех пользователей в нашей системе.
Если у нас запущена служба ``Жёлтых Страниц'' (Yellow Pages), то вам, возможно, надо будет другим образом получить информацию о пользователях:
ypcat passwd | perl -F: -lane 'print $F[0]'
Здесь команда ypcat выдает в поток стандартного вывода файл похожий на passwd, который может быть обработан Perl так же как и локальный /etc/passwd.
Во всех наших упражнениях мы получали только имена пользователей, а
не их номера (userID). Номер пользователя (userID) находится в третьей
колонке, или $F[2]
в нашем скрипте.
perl -F: -lane 'print $F[2]' /etc/passwd
Мы получили список номеров, теперь надо определить максимальный и напечатать следующий максимальный номер.
Для этого заведём скалярную переменную $max
. Изначально,
$max
не определена, она будет равняться нулю в сравнениях с числами.
Наша работа состоит в сравнении каждого номера пользователя (userID) с
$max
и установке $max
в значение номера, если он больше.
perl -F: -lane '$max = $F[2] if $max < $F[2]; \ print $max' /etc/passwd
Здесь мы присваиваем $max
значение $F[2]
, как только
выполнилось условие
$max < $F[2]
Это происходит на каждом шаге цикла по строчкам файла
/etc/passwd
Изобразим это равнозначным скриптом:
#!/usr/bin/perl $\ = $/; while (<>) { chop; @F = split /:/; $max = $F[2] if $max < $F[2]; print $max; }
Это уже почти то, что нам надо. Однако нам все еще надо передавать данные из /etc/passwd в скрипт, что не совсем удобно. Давайте внесем открытие файла /etc/passwd внутрь скрипта.
#!/usr/bin/perl open(PASSWD,"/etc/passwd"); $\ = $/; while (<PASSWD>) { chop; @F = split /:/; $max = $F[2] if $max < $F[2]; print $max; }
Директива open
создаёт дескриптор файла, ассоциированный с
/etc/passwd для чтения.
Если у вас используются Yellow Pages, то для этого случая решение чуть-чуть длиннее:
#!/usr/bin/perl open(PASSWD,"ypcat passwd|"); $\ = $/; while (<PASSWD>) { chop; @F = split /:/; $max = $F[2] if $max < $F[2]; print $max; }
Здесь Perl использует вместо файла вывод команды. Признаком команды является вертикальная черта в конце. Это напоминает оператор перенаправления, который мы использовали при написании command-line версии программы.
Вывод наших последних нескольких программ представлял собой серию
чисел, представляющих максимальный userID, который мы находили на
каждом шаге. На самом же деле, нам нужно только последнее число.
Как это сделать в программе? Просто вынесем print
из цикла.
#!/usr/bin/perl open(PASSWD,"/etc/passwd"); # or YP equivalent $\ = $/; while (<PASSWD>) { chop; @F = split /:/; $max = $F[2] if $max < $F[2]; } print $max + 1;
Не забудем прибавить 1, чтобы получить число большее, чем предыдущее максимальное.
Теперь сохраним этот скрипт в файле, поставим на нём бит
исполнимости и поместим его куда-нибудь в $PATH
. Потом, когда нам
понадобится новый номер пользователя, просто вызовем этот скрипт,
заключив его имя в обратные кавычки, и получим правильный номер.
Или почти правильный номер. Некоторые системы (типа SunOS, на которых это
тестировалось), имеют пользователя nobody
, который имеет максимально
возможный номер, например, 65535. Если мы запустим нашу программу, то
получим не то, что хотели.
Поступим просто -- исключим большие номера, например, большие 30000,
т.е. $max
будет изменяться только если $F[2]
меньше
30000. Нам всего лишь придётся усложнить правую часть if
:
#!/usr/bin/perl open(PASSWD,"/etc/passwd"); # or YP equivalent $\ = $/; while (<PASSWD>) { chop; @F = split /:/; $max = $F[2] if $F[2] < 30000 and $max < $F[2]; } print $max + 1;
Вот, с этой задачей покончили. Как минимум, она работает на SunOS.
Так что решение этой маленькой задачи получилось не таким уж маленьким, но по крайней мере мы выполнили ее с помощью нескольких строк на Perl. Теперь, если вы не боитесь длинных командных строк, то давайте переведём эту программу в форму командной строки:
perl -aF: -lne '$m=$F[2] if $F[2]<30000 and $m<$F[2];\ END { print $m+1 }' /etc/passwd
Здесь интересным свойством является то, что блок END
автоматически перемещается за пределы неявного цикла, помещая его там, где
он находится в скрипте.
Если вы новичок в Perl, то вы, вероятно, захотите найти хорошую книгу. Есть две книги, которые я могу порекомендовать вам, хотя я иногда некритичен к ним, поскольку участвовал в написании обеих.
Learning Perl (O'Reilly and Associates, ISBN 1-56592-042-2) постепенное введение в язык, вместе с упражнениями и прокомментированными ответами. Рассчитана на людей ``знакомых с UNIX, но никоим образом не Гуру'', хотя, определённо, у вас уже должны быть базовые знания по программированию перед тем, как вы откроете эту книгу.
Programming Perl (O'Reilly and Associates, ISBN 0-937175-64-1) это большой всеобъемлющий справочник по всему языку, соавтором книги является создатель Perl Ларри Уолл (Larry Wall). Там Вы встретите также некоторое количество учебного материала и много длинных практических примеров. Тем не менее, книга предназначена для Гуру, и если вы не программируете в UNIX с 1977 года, как я, она может просто влететь вам в одно ухо и вылететь из другого. (c) LVK
Кроме того, есть очень хорошая группа новостей в Usenet, называемая comp.lang.per
lee7 говорит, что теперь уже comp.lang.perl.misc, которую читают очень высококлассные специалисты в Perl, включая Ларри Уолла (и вашего покорного слугу). Если у вас имеются сложности с доступом к Usenet, то вместо этого вы можете послать письмо на адрес perl-users-request@virginia.edu и попросить, чтобы вас подписали на список рассылки.