Однострочник месяца на Perl: Дело о
совпадающих UID
Автор: Ben
Okopnik
Перевод: Павел Соколов
Электронное письмо было кратким и переходило сразу к делу:
Вумерт, Буду краток и сразу перейду к делу. Слияние трёх компаний. Нервный сисадмин. 3000+ пользователей. /etc/passwd. UIDы. Всего лучшего, Фринк Ублик
Вумерт Фунли, Реалистичный Компьютерный Детектив, ухмыльнулся: ответы клиента во время разговора по телефону были громкими и бессвязными, а сам разговор пестрел фразами вроде "Не работает!" и "Помогите!". Вумерт послал Фринка на место, чтобы осмотреться, и письмо стало успешным результатом этой рекогносцировки. Всё, что осталось, -- это найти решение, имея в запасе всего несколько часов, перед тем, как клиент завершит рабочий день. Вумерт решил использовать время продуктивно. Так, где его любимая подушка?
Освежённый и во всеоружии, Вумерт появился у клиента и сразу же столкнулся со взвинченным Фринком.
- Вумерт, это ужасно! Файл слишком длинный для ручного поиска, и это всё только множество UID. Сисадмин кается, бесится и паникует по очереди, а от его волос уже практически ничего не осталось. И что мы сможем здесь сделать?
- Нет проблем, дружище... ой, извини. Я вернулся из Канберры всего несколько часов назад и некоторое влияние всё ещё чувствуется. Могу сказать по своему ужасному опыту, что завтра будет ещё хуже: я должен быть утром в Далласе, после обеда в Нью-Йорке, а вечером - в Тель Авиве. Я посоветовал бы тебе надеть наушники или исчезнуть из моих окрестностей, пока акцент не выветрится. О-о-о, эти опасности путешествий...
Фринк заметно расстроился.
- Вумерт, ты не принимаешь ситуацию всерьёз. Разве ты не видишь, что это огромная проблема?
- А, это? Расслабься, не принимай к сердцу. Это совсем не так плохо, Фринк, как выглядит; на самом деле...
Вумерт проворно извлёк из кармана свои любимые перчатки для печати и натянул их.
- ...С Perl это просто тривиально. Что нам надо сделать - так это дать сисадмину пару инструментов для командной строки, которыми он может воспользоваться для решения этой проблемы, и - так как он использует bash - он сможет вытаскивать их клавишей "верх" каждый раз, как они ему понадобятся. Ну вот!
perl -F: -walne'$h{$F[2]}.="$F[0] ";END{$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}' /etc/passwd
Список совпавших UID, вместе с соответствующими им именами пользователей пробежал по экрану после того, как Вумерт нажал "Enter". Оба, Вумерт и Фринк, отметили с интересом, что для UID0 было три записи -
0: root sashroot kill3r
- Так-так. Кажется кому-то удалось влезть и создать себе запись UID0 (root). "sashroot" - нормально, это "standalone shell" (обособленная оболочка) для восстановительных работ, но "kill3r"? Хорошо, мы сообщим клиенту; а тем временем продолжим с нашей проблемой. У сисадмина теперь есть список всех дубликатов - а их, кажется, не так много - но поиск следующего свободного UID может быть проблемным. Так что вот второй инструмент:
perl -wle'{getpwuid++$n&&redo;print$n}'
- Это должно дать ему хороший задел в решении этих проблем. Что до нас - мы направляемся домой!
Когда они вернулись в дом к Вумерту и расположились перед камином - ночь была холодной и ветреной - Фринк выжидающе посмотрел на Вумерта. Заметив взгляд, Вумерт рассмеялся:
- Знаю, знаю. Мне следует объясниться, не так ли? Ореол тайны - штука приятная, но это ничто перед удовольствием от обучения. Вот, начнём с первой строчки:
perl -F: -walne'$h{$F[2]}.="$F[0] ";END{$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}' /etc/passwd
- Во-первых, взгляни на ключи командной строки, которые я использовал:
-w Включить предупреждения -a Авторазделение (см. "-F") -l Включить обработку конца строки -n Неявный непечатаемый цикл -e Выполнить следующие команды -F: Использовать ":" в качестве разделителя для ключа авторазделения "-a"
Если ты помнишь наше последнее приключение, все перечисленные ключи, кроме "-a" и "-F" уже знакомы тебе. Авторазделение разбивает строчки, прочитанные "-n" или "-p", используя пробельные символы (whitespace) [1] в качестве разделителя по умолчанию и сохраняя результат в массив "@F". Ключ "-F" опционально переопределяет символ разделителя.
Так как мы читаем файл "/etc/passwd", давай взглянем на формат его отдельных строк:
borg:x:1026:127:All your base are belong to us!:/home/borg:/bin/bash
Здесь семь стандартных полей, расположенных в порядке "имя - пароль - UID - GID - информация о пользователе - домашняя директория - оболочка". Сейчас нас интересуют только имя и UID; что я собираюсь сделать - это построить хэш (hash) или словарный массив - это очень важная структура данных в Perl, одна из трёх базовых - который будет содержать UID (3е поле) как индекс (key), и имя (1-е поле), за которым следует пробел, как значение (value), для каждой записи в "/etc/passwd":
$h{$F[2]}.="$F[0] "
Так как имена пользователей не могут содержать пробелов, его
можно использовать в качестве разделителя. Когда предыдущий цикл
завершит работу, я просканирую хэш и распечатаю все значения, в
которых после пробела есть ещё какой-нибудь символ:
$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}
Я смотрю, ты всё ещё выглядишь озадаченным. Давай я напишу это в более читабельной форме:
for ( keys %h ){ # Цикл по хэшу "%h" if ( $h{$_} =~ / ./ ){ # В значении есть пробел, за которым есть ещё символ? print "$_: $h{$_}\n"; # Если да, распечатать UID, двоеточие, пробел и значение } }
Если ты поразмыслишь, ты увидишь, что единственная вещь, которая может подойти под регулярное выражение - это значение, в котором содержится более одного имени, то есть в случае совпавшего UID.
- Всё правильно, теперь я могу видеть результат. А что там со второй командой, инструментом "следующий свободный UID"?
- А, ты имеешь в виду эту:
Это не более, чем короткий цикл, в котором я проверяю, существует ли UID, указанный в "$n". Если команда срабатывает нормально, что значит, что уже есть используемый UID, равный "$n" - вызывается "redo", "$n" увеличивается на единицу и тест снова выполняется. Если же команда выпадает с ошибкой, "$n" распечатывается в STDOUT и программа завершается. Полезно и несложно. Немного работы и они со всем разберутся. Вот взлом системы безопасности - дело другое, но, по крайней мере, они о нём знают...
[1] Прим. пер. Имеются в виду символы вроде табуляции и перевода каретки, которые не отображаются, но управляют выводом.
Copyright © 2002, Ben Okopnik. Copying
license http://www.linuxgazette.com/copying.html
Published in Issue 85 of Linux Gazette, December
2002