[lg85]: Однострочник месяца на Perl: Дело о совпадающих
UID
Linux Gazette на русском | Выпуск #137 |
Тираж 8802 экз.
"Мы не двойняшки, мы тройняшки!"
(c) "Ералаш"
Здравствуйте! Сегодня Вашему вниманию предлагается продолжение цикла
"Однострочник месяца на Perl". В нём решаются вопросы борьбы с
дублирующимися UID и поиском первого свободного UID. Рекомендуется для
прочтения любителям творчества Шерлока Холмса, доктора Ватсона,
Василия Ливанова и Виталия Соломина. Ну, и тем, кто запарился
отлавливать в /etc/passwd двойняшек. ;-)
Электронное письмо было кратким и переходило сразу к делу:
Вумерт,
Буду краток и сразу перейду к делу.
Слияние трёх компаний.
Нервный сисадмин.
3000+ пользователей.
/etc/passwd.
UIDы.
Всего лучшего,
Фринк Ублик
Вумерт Фунли, Реалистичный Компьютерный Детектив, ухмыльнулся:
ответы клиента во время разговора по телефону были громкими и
бессвязными, а сам разговор пестрел фразами вроде "Не работает!" и
"Помогите!". Вумерт послал Фринка на место, чтобы осмотреться, и
письмо стало успешным результатом этой рекогносцировки. Всё, что
осталось, -- это найти решение, имея в запасе всего несколько
часов, перед тем, как клиент завершит рабочий день. Вумерт решил
использовать время продуктивно. Так, где его любимая подушка?
Глава 2
Освежённый и во всеоружии, Вумерт появился у клиента и сразу же
столкнулся со взвинченным Фринком.
- Вумерт, это ужасно! Файл слишком длинный для ручного поиска, и
это всё только множество UID. Сисадмин кается, бесится и паникует
по очереди, а от его волос уже практически ничего не осталось. И
что мы сможем здесь сделать?
- Нет проблем, дружище... ой, извини. Я вернулся из Канберры
всего несколько часов назад и некоторое влияние всё ещё
чувствуется. Могу сказать по своему ужасному опыту, что завтра
будет ещё хуже: я должен быть утром в Далласе, после обеда в
Нью-Йорке, а вечером - в Тель Авиве. Я посоветовал бы тебе надеть
наушники или исчезнуть из моих окрестностей, пока акцент не
выветрится. О-о-о, эти опасности путешествий...
Фринк заметно расстроился.
- Вумерт, ты не принимаешь ситуацию всерьёз. Разве ты не видишь,
что это огромная проблема?
- А, это? Расслабься, не принимай к сердцу. Это совсем не так
плохо, Фринк, как выглядит; на самом деле...
Вумерт проворно извлёк из кармана свои любимые перчатки для
печати и натянул их.
- ...С Perl это просто тривиально. Что нам надо сделать - так
это дать сисадмину пару инструментов для командной строки, которыми
он может воспользоваться для решения этой проблемы, и - так как он
использует bash - он сможет вытаскивать их клавишей "верх" каждый
раз, как они ему понадобятся. Ну вот!
Список совпавших UID, вместе с соответствующими им именами
пользователей пробежал по экрану после того, как Вумерт нажал
"Enter". Оба, Вумерт и Фринк, отметили с интересом, что для UID0
было три записи -
0: root sashroot kill3r
- Так-так. Кажется кому-то удалось влезть и создать себе запись
UID0 (root). "sashroot" - нормально, это "standalone shell"
(обособленная оболочка) для восстановительных работ, но "kill3r"?
Хорошо, мы сообщим клиенту; а тем временем продолжим с нашей
проблемой. У сисадмина теперь есть список всех дубликатов - а их,
кажется, не так много - но поиск следующего свободного UID может
быть проблемным. Так что вот второй инструмент:
perl -wle'{getpwuid++$n&&redo;print$n}'
- Это должно дать ему хороший задел в решении этих проблем. Что
до нас - мы направляемся домой!
Глава 3
Когда они вернулись в дом к Вумерту и расположились перед
камином - ночь была холодной и ветреной - Фринк выжидающе посмотрел
на Вумерта. Заметив взгляд, Вумерт рассмеялся:
- Знаю, знаю. Мне следует объясниться, не так ли? Ореол тайны -
штука приятная, но это ничто перед удовольствием от обучения. Вот,
начнём с первой строчки:
- Во-первых, взгляни на ключи командной строки, которые я
использовал:
-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"?
- А, ты имеешь в виду эту:
perl -wle'{getpwuid++$n&&redo;print$n}'
Это не более, чем короткий цикл, в котором я проверяю,
существует ли UID, указанный в "$n". Если команда срабатывает
нормально, что значит, что уже есть используемый
UID, равный "$n" - вызывается "redo", "$n" увеличивается на единицу
и тест снова выполняется. Если же команда выпадает с ошибкой, "$n"
распечатывается в STDOUT и программа завершается. Полезно и
несложно. Немного работы и они со всем разберутся. Вот взлом
системы безопасности - дело другое, но, по крайней мере, они о нём
знают...
[1] Прим. пер. Имеются в виду символы вроде табуляции и перевода
каретки, которые не отображаются, но управляют выводом.
Команда переводчиков: Александр Куприн, Андрей Киселев, Александр Михайлов, Александр Саввин,
Владимир Меренков, Иван Песин, Игорь Яровинский, Павел Соколов, Роман Шумихин,
Сергей Скороходов, Юрий Прушинский
Со всеми предложениями, идеями и комментариями обращайтесь к
Александру Куприну (ru_classic at mail.ru). Убедительная
просьба: указывайте сразу, не возражаете ли Вы против публикации
Ваших отзывов в рассылке.