The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Парсинг на Perl (perl parser)


<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>
Ключевые слова: perl, parser,  (найти похожие документы)
From: Иван Рак <rak at tut.by> Newsgroups: http://rak.at.tut.by Date: Mon, 20 Sep 2004 18:21:07 +0000 (UTC) Subject: Парсинг на Perl Оригинал: http://rak.at.tut.by/col01r.txt Unix Review Column 1 (Март 1995) Basic Parsing Randal L. Schwartz http://www.stonehenge.com/merlyn/UnixReview/col01.html Перевод: Иван Рак (rak@tut.by) Основы анализа. Perl быстро становится ключевым инструментом обычного системного администратора и волшебной шляпой системного программиста. Легко, однако, испугаться 211 страниц документации, которая прилагается к последнему (пятому) релизу Perl. Быть может, вы уже спрашиваете себя "с чего начинать?" и "сколько всего надо знать, чтобы писать программы на Perl?" Легче всего - посмотреть, как кто-то другой решает простую проблему. Возьмем для примера типичную задачу системного администрирования - присвоение новому пользователю уникального ID номера. Для этого нужно определить наибольший из имеющихся в вашей системе ID, и выбрать следующее большее число. Разберем подвернувшуюся нам задачу на простые задачи и их решения. [? We'll build up to the task at hand by looking at some simpler problems and their solutions.] Во-первых, посмотрим, как распечатывается первая колонка вывода команды who, [just for grins]. who | perl -ne '@F = split; print "$F[0]\n";' Вывод who передается на ввод Perl. Ключ -n позволяет выполнить некоторый код, помещая каждую входящую строку в переменную $_. Ключ -e задает код, и мы можем (и часто будем) совмещать ключи показанным образом. В нашем случае вы имеем два выражения: операции split и print. split разбивает содержимое $_ на список слов (подразумевая разделителем между словами пробел). Результат получает массив @F. Затем операция [? operation. операция - плохо, потому что похоже на оператор, а это функция] print отображает значение первого элемента масства, завершенное переводом строки (\n). Заметим, что доступ к первому элементу @F происходит через $F[0], потому что элементы нумеруются, начиная от нуля (как в массивах C). Можно немного сэкономить на наборе, если вынести разделение в аргументы командной строки: who | perl -ane 'print "$F[0]\n"' Заметим, что здесь мы добавили ключ -a, который заставляет Perl разбивать содержимое $_ в @F автоматически, так же, как в предыдущем примере мы сделали это явно. Чтобы набирать еще чуть меньше, можно добавить ключ -l, который делает две вещи сразу: - удаляет перевод строки из переменной $_ перед тем, как ее увидит наш код (на самом деле его (код) не волнует, есть ли он (перевод) там (в строке)), и - приклеивает перевод строки обратно на выходе. После этого наш маленький командно-строковый пример будет выглядеть так: who | perl -lane 'print $F[0]' И, чтобы сократить еще чуть-чуть, заменим ключ -n ключом -p, который позволяет печатать то, что получилось в $_ в конце кода: 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 } Как вы видите, немаленький кусок кода [] можно задать несколькими символами в командной строке. Переменная $\ определяет заканчивающий суффикс для каждой операции print, примерно так, как это делает переменная ORS а awk. По умолчанию она пуста, то есть выводимая строка будет выглядеть так, как задано. Здесь мы придаем этой переменной значение $/, разделителя входящих записей (как RS в awk). По умолчанию это "\n". То есть разделитель вывода такой же, как разделитель ввода, и к печатаемому будет добавляться перевод строки. Закончим, наконец, с командой who. Перейдем к реальной задаче: проход по файлу паролей для получения наибольшего пользовательского ID. Файл паролей отличается от вывода команды who - здесь колонки разделяются не пробелами, а двоеточием. Нет проблем - укажем другой символ-разделитель: perl -aF: -lne 'print $F[0]' /etc/passwd и мы получим список пользователей на стандартном выводе. Ключ -F задает двоеточие как разделитель. Заметим, что мы поставили ключ -a перед -F, что, я думаю, вполне логично -- разделитель полей не имееет смысла, если их не разделять. Если у вас запущены Желтые Страницы [Yellow Pages], то есть, я хотел сказать, Network Information Services, вам, возможно, понадобится вытягивать пароли отсюда, а не из файла, чтобы получить что-то полезное: ypcat passwd | perl -aF: -lne 'print $F[0]' Здесь команда ypcat выдает пароле-подобный файл на стандартный вывод, где команда Perl радостно его слизывает, как если бы это был локальный файл etc/password. Но это имена пользователей, не пользовательские ID. Они в третьем столбце, в $F[2] (опять же, сдвинуто на один, потому что отсчет начинается с нуля). Немного подредактируем, и: perl -aF: -lne 'print $F[2]' /etc/passwd Теперь у нас есть список чисел. Уже лучше. Нам нужно определить наибольшее число, и распечатать еще большее. Для этого используем скалярную переменную $max. Изначально $max не определена, и при сравнении с другими числами будет выглядеть как ноль. Итак, работа состоит в том, чтобы сравнить номер каждого пользователя с $max, и присвоить $max этот номер, если он больше. perl -aF: -lne '$max = $F[2] if $max < $F[2]; print $max' /etc/passwd Здесь мы присваиваем $max значение, если выполняется условие. В данном случае условие $max < $F[2] вычисляется на каждой итерации цикла, и, если результат истина, происходит присваивание. Это единственное место в Perl, где логическая последовательность идет справа налево, а не слева направо. Теперь это все становится неудобно длинным, так что лучше развернуть в скрипт: #!/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() создает дескриптор [? filehandle. глупо, на самом деле, переводить handle как "дескриптор"] для чтения файла /etc/passwd. Ваше, желтостраничники [YP'ers], решение будет на пару символов длиннеее: #!/usr/bin/perl open(PASSWD,"ypcat passwd|"); $\ = $/; while (<PASSWD>) { chop; @F = split /:/; $max = $F[2] if $max < $F[2]; print $max; } Perl чУдно использует вывод команды как файл. О том, что это команда, а не файл, свидетельствует завершающая вертикальная черта. Это напоминание о потоке [? pipe. не уверен.], который используется, когда мы пишем программу в командной строке. На выходе этих последних программ будет серия чисел с наибольшим найденным пользовательский ID. На самом деле нам нужно распечатать самый последний номер. Нет, еще раз. На самом деле нам нужен номер, больший на единицу. Как это сделать в программе? Просто. Просто вынесем печать из цикла: #!/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, чтобы получить больше, чем прошлое наибольшее. Whew! Мы можем набить этот скрипт в файл, преобразовать в исполнимый код, поместить его где-нибудь в $PATH, и, когда нам нужен новый номер пользователя, просто вызовем его в обратных кавычках [уточнить], и получим правильное значение. Или что-то около правильного номера. Как оказалось, некоторые системы (например, SunOS, на которой я это тестировал), имеют пользователя nobody, с очень-очень большим ID. Если вы запустите эту программу в своей системе и получите что-то вроде 65535, у вас такой тоже есть. Так что нам нужно исключить из нашего подсчета все, что выше какого-то порога. Как же это сделать? Допустим, $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 -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) - нежное введение [? gentle introduction] в язык, с примерами и развернутыми ответами. Это книга для тех, кто "знаком с UNIX, но никак не гуру". Но требует некотрого знания снов программирования. Programming Perl (O'Reilly and Associates, ISBN 0-937175-64-1) - здоровенный всесторонний справочник по языку, в соавторстве с создателем Perl, Ларри Уоллом. Здесь вы найдете немного поверхностной обучающей информации, и массу длинных практических примеров. Однако это скорее книга для гуру, и может пролететь мимо головы, если вы на хакаете UNIX с 1977, как я. Еще есть очень интересная Usenet-группа comp.lang.perl, с большим участием волшебников Perl, включая Ларри Уолла (и вашего покорного слугу). Если у вас нет доступа к Usenet, пошлите е-мэйл на perl-users-request@virginia.edu, и попросите включить вас в список рассылки.

<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>

 Добавить комментарий
Имя:
E-Mail:
Заголовок:
Текст:




Спонсоры:
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2021 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру