В 19 выпуске Perl-журнала PragmaticPerl опубликованы две мои статьи: Постепенная автоматизация рутинных задач, Постепенная автоматизация в примерах. Здесь находится дополняемая и улучшаемая версия первой из них.
Бывает так:
есть несложная последовательность операций (в командной строке, разумеется), которую
приходится время от времени повторять;
хорошо бы написать нормальный скрипт, да все руки не доходят,
и времени жалко, да и на самом деле не такое уж плевое дело – аккуратно запрограммировать эту рутину,
а задача опять должна быть выполнена “вот прям сейчас же”,
и ни минуты лишней нет.
И повторяются и повторяются однотипные действия:
mkdir, mysql_install_db, chown, vim my.cnf, service start, create database, grant all privileges ...
Знакомая картина? Как насчет что-нибудь изменить? Хочу рассказать про один метод как раз для таких случаев.
Итак, автоматизация рутины. На что обычно похожа такая задача?
Последнее обстоятельство – ограниченность времени – очень важно. Про опасности прерывистого времени для разработчиков пишут много (комикс, еще примеры см. в конце статьи), но иногда это просто реальность. В таких услових разработку полезно организовать так, чтобы она состояла из серии маленьких последовательных доработок, каждая из которых занимает… Скажем, от 15 минут до часа, не больше.
Так как главный получатель выгоды – вы сами, в ваших собственных интересах сделать первый прототип очень быстро, хотя бы и в ущерб полной функциональности: первый успех воодушевит на дальнейшие доработки, а хотя бы частичная автоматизация сэкономит вам время.
Сверхбыстрое прототипирование без сложных алгоритмов, вызов внешних программ, ssh, проверки существования файлов и т.п. – это область, в которой силен шелл (shell). Однако большие шелл-программы тяжелы в отладке и доработке, так что в какой-то момент стоит перейти от шелльного прототипа к Perl.
Вот и готов метод “постепенной автоматизации”: быстрый первый прототип на шелле, серия коротких самодостаточных доработок, своевременный переход на Perl. Пожалуй, это сработает и с другим интерпретируемым языком вроде Ruby или Python, но переход от шелла к универсальному языку самый гладкий именно с Perl, надо пользоваться этим.
В этой статье я опишу пошаговый рецепт постепенной автоматизации и свои соображения “почему это работает именно так”. В соседней статье – пример применения этого алгоритма.
Хочу предупредить: в этих шагах нет ничего сверхъестественно сложного или неожиданного, возможно, вы уже давно так и действуете. С другой стороны, еще не все прониклись идеей постепенных улучшений, так что думаю, что лишней статья тоже не будет.
Каждый шаг разработки почти ничего не стоит, не требует принятия сложных решений, и при этом делает рутинную задачу ощутимо более легкой по сравнению с предыдущим шагом.
Каждая стадия порождает пригодный к использованию инструмент, при этом важные фичи и свойста реализуются в первую очередь, поэтому в любой момент можно остановиться и получить инструмент как раз той степени проработанности, которая требуется для задачи, и ровно с теми затратами времени и усилий, которых эта задача заслуживает.
Полезно, чтобы между последовательными шагами проходило время, достаточное по крайней мере для нескольких применений вашего нового инструмента. Так вы получите ценный фидбек, столкнетесь с особыми случаями, которые сможете учесть в новых версиях.
И помните: после любого шага можно остановиться – все равно у вас в руках останется пригодный к использованию инструмент.
Итак, поехали. Дано: имеем задачу, которая в принципе решается в командной строке.
Первым делом убеждаемся, что действительно можем решить задачу вручную: открываем шелл и выполняем все необходимые команды.
Приобретения первого шага:
Небольшая последовательность команд легко склеивается в шелльный однострочник.
Выполнение действий одно за другим обеспечивается точкой с запятой ;
,
выполнение при условии успеха или неудачи предыдущей команды – &&
и ||
,
также полезны перенаправление вывода, пайпы и фоновое выполнение.
Интерактивное редактирование обычно несложно заменяется на sed -i
,
генерация текста по шаблону – tpage
,
повторение однообразных действий реализуется циклами for
и while
.
Если у получившегося микро-скрипта нет параметров или они устроены очень просто – однострочник можно сделать шелльным алиасом и вызывать по имени.
Приобретения второго шага:
Ctrl-r
и запустить снова
(возможно, подправив некоторые параметры);Если последовательнось действий оказывается длинее 5-6 операций или включает в себя сложное ветвление – можно после ручного выполнения сразу перейти к 3 шагу.
Команды, выполняемые вручную в шелле, играючи превращаются в шелльный скрипт: копируем команды из истории и сохраняем их в отдельном файле.
Как и для однострочника, пригодятся &&
, ||
, sed -i
и tpage
.
Кроме того, удобнее, чем в однострочнике, использовать циклы, переменные, временные файлы.
Появляется возможность передавать в скрипт параметры (лучше ограничиться позиционными).
Приобретения третьего шага:
Этот этап – прототипирование на шелле – стоит пройти, даже если сразу понятно, что в дальнейшем понадобится скрипт на Perl.
Во-первых, шелльные конструкции для манипуляций с процессами и файлами еще более емкие, чем в Perl, поэтому прототип можно сделать совсем быстро.
Во-вторых, нет соблазна преждевременно заняться несущественным: структурой классов, сложной валидацией и т.п. А лишний раз отвлекаться при автоматизации рутины очень опасно: время ограничено, и если не займетесь в первую очередь главным – рискуете остаться безо всякого рабочего инструмента. На всякий случай уточню: речь идет о быстрой автоматизации рутинных ручных действий. Для сложных программ и библиотек архитектура вообще и структура классов в частности не являются несущественными деталями.
Шелльный скрипт очень легко превратить в простецкий Perl-скрипт:
вызовы внешних программ заменить на qx()
,
if
-ы и while
-ы заменить на Perl-овые,
аргументы командной строки $1
, $2
заменить на $ARGV[0]
, $ARGV[1]
и т.д.
Сразу же стоит добавить use strict
и use warnings
.
При всей своей простоте этот шаг, пожалуй, самый неочевидный из всей последовательности: велик соблазн выкинуть шелльную версию и сразу начать писать “взрослый” Perl, с модулями, плагинами, валидацией и инкапсуляцией. Однако я очень (очень) рекомендую все-таки сдержать себя и начать с “наивного” Perl. Во-первых, у вас уже есть шелльный скрипт, отлаженный на реальных случаях. Глупо просто так терять все знания, собранные в нем. Во-вторых, переписать скрипт с нуля – дольше, чем переделать shell в Perl. А долгая разработка – это риск вообще ее не закончить.
Приобретения четвертого шага:
Вообще-то желательно перейти от шелльного скрипта к Perl-овому достаточно рано, пока скрипт не вырос и не обзавелся сложными конструкциями. Но даже если вам в наследство достался уже достаточно большой и сложный шелльный скрипт, в котором понадобилось сделать существенные улучшения, поступите с ним как с прототипом с шага 2. Вместо того, чтобы с нуля переписывать скрипт на Perl (или другой язык), сделайте “механический” подстрочник. Это действительно просто и не требует сложных решений, и ничего из заковыристой логики не потеряется. А затем уже беритесь рефакторить получившийся “автоперевод”.
Начинаем улучшать бесхитростный Perl-скрипт, полученный на предыдущем шаге. Рекомендую действовать в таком порядке:
use Getopt::Long;
именованные параметры командной строки.-h
выдаем простейшую справку: самые типичные примеры использования.
О подробном описании параметров пока можно не заботиться, все равно они еще поменяются.Что получаем на этом шаге:
Перечислю очевидные улучшения и преимущества, которые они дают. Выбирайте то, что для вас важнее, дополняйте список своими любимыми рефакторингами.
grep
, sed
, awk
заменить на внутренние grep
, map
– скорость и переносимость;Если задача совсем админская (установка ПО, активная работа с удаленными серверами, однотипная обработка многих серверов) – рассмотрите фреймворки Rex, Fabric, Ansible.
Если разработка небольшой автоматизации дошла до этого шага,
значит, автоматизация оказалась (или стала) не такой уж небольшой ^_^
Стоит подумать над тем, чтобы выложить свою утилиту в open source и порекламировать в сообществе: если инструмент так хорошо работает для вас, значит, может пригодиться еще кому-нибудь.
Приобретениями могут стать всенародная слава и любовь, обширный фидбек, сторонние пулл-реквесты, темы для блога и выступлений на конференциях.
О постепенной (инкрементальной, итеративной) разработке тоже много пишут (например, см. раздел “Succession” здесь, еще ссылки в конце статьи). Самое важное для наших задач:
Почему это хорошо работает:
Хочу еще написать о связке “прототипе на шелле” – “скрипт на Perl”. Довольно часто встречаются мнения “зачем мне шелл, у меня есть Perl” или наоборот “Perl? Да ну, зачем, на баше напишу”.
По-моему, надо использовать и тот, и другой – в тех ситуациях, где проявляются их сильные стороны:
И если вам досталось поддерживать и дорабатывать большой и сложный шелльный скрипт – рассматривайте его как готовый прототип, “протранслируйте” на Perl и рефакторите дальше.
Еще раз отмечу, что описанный метод касается легковесной автоматизации ручных действий. Для промышленной разработки сложного и отказоустойчивого кода можно попробовать обойтись без сверхбыстрого прототипирования, зато на ранних стадиях стоит оценить требования по производительности и надежности, продумать архитектуру и схему данных.
И напоследок пара слов о происхождении “постепенной автоматизации”.
Простота манипуляций с файлами и процессами,
отсутствие границы между интерактивным шеллом и интерпретатором шелльных скриптов,
бесшовная интеграция шелла и Perl-а – все это уже давно встроено в Unix и Unix-подобные
системы, шеллы и Perl соответственно, нам остается просто использовать
эти возможности для накапливаемых пошаговых улучшений своих программ.
Так что большое спасибо Кену, Брайану, Стивену и Ларри2 ^_^
Удачных автоматизаций!
О вреде прерываний для программистов:
О пользе постепенности:
Фреймворки для легковесной автоматизации администрирования: