google web font

пятница, 11 февраля 2011 г.

Как сделать, чтоб ничего не делать

Компьютер — замечательная вещь, позволяющая эффективно решать проблемы, которые без него вообще не возникли бы (© афоризм). По долгу работы мне приходится часто вбивать одну и ту же последовательность команд в линуксовую консоль, чтобы эффективно решить проблему. Наиболее банальные последовательности быстро превратились в алиасы. Алиасы многократно вводятся путём нажатия стрелки вверх и кнопки Enter, что заставило меня проникнуться понимаем основ цикла for непосредственно в командной строке (это выглядит примерно так: for X in $(seq 1 10); do useful_alias; done). В общем-то, большая часть bash_history примерно из такого мусора и состоит, но есть вещи, которые автоматизированы чуть более весело. Итак, есть несколько способов заставить консоль саму выполнять нужную последовательность команд.


1. alias. Не все используют такой вариант: в алиас можно записать больше, чем одну команду, причём команде, записанной последней, можно передавать параметры, как будто это не алиас вовсе. Кроме того, алиас может ссылаться на другой алиас. Например, у меня есть демон, который мне регулярно приходится перезапускать, удаляя при этом его логи. Сначала я сделал алиас для остановки, алиас для удаления логов и алиас для запуска и запускал их руками в нужном порядке (давно это было). Это было что-то вроде:
alias start='./etc/rc.d/daemon start'
alias stop='./etc/rc.d/daemon stop'
alias clr=': >'
alias clr_log='clr ./var/log/daemon.log'
С тех пор много воды утекло, сначала я запускал stop && clr_log && start, а потом написал тупо ещё один alias сверху:
alias re='stop && clr_log && start'
Напоминаю, к чему я клоню: в алиасах могут запускаться другие алиасы. Кроме того, если запускать его с аргументами, то они будут переданы последней в алиасе команде, как будто вместо алиаса написана полная развёрнутая последовательность команд. Это значит, что я могу перезапускать своего демона такой командой
$ re debug valgrind
* daemon [PID] stopped
* starting daemon...
* in debug mode
* under valgrind
* daemon [PID] started
Алиас прописывается в файл ~/.bashrc для себя любимого, или, если для всех пользователей системы, в /etc/bash/bashrc.


2. функции. Не очень популярный вариант — шелловские функции народ предпочитает использовать в "пакетных файлах" — скриптах, однако на самом деле функция по способу применеия не отличается от алиаса и даже встроенной или внешней команды. У меня есть несколько таких, большинство было изначально алиасами, но потом превратились в функции. Один из примеров. Был алиас
alias show_clean_log='filter.pl ./www/error_log | less'
Запускал скрипт, который вычищал из апачевского лога таймстампы и информацию о рефераллах. Потом мне этого стало недостаточно и алиас превратился в функцию:
function show_log {
    if [ -n "$1" ]; then
        filter.pl ./www/error_log > "$1"
    else
        filter.pl ./www/error_log | less
    fi
}
Эта функция работает как раньше работал алиас, но теперь если ей дать имя файла, то она запишет лог в него, а больше никаких изменений в порядке вызова нет. Есть другие функции, в основном я в них пишу то, что слишком коротко для скрипта, но не влезает в алиас; но много познавательных примеров набрать не смогу.
Функции прописываются так же, как и alias, либо в персональный, либо в системный bashrc.

3. Скрипты. Самый популярный способ после алиасов, если даже не популярнее. Все пишут скрипты по каждому поводу, даже если писать скрипт нет никакой необходимости. Я видел «дельный» совет по написанию скрипта для логина по ssh на машину при наличии огромного парка машин. Там был скрипт вида
#!/bin/bash
ssh "$0"
и предлагалось в каталоге, который входит в $PATH, создавать на этот скрипт симлинки с именами типа user@server15. Я тогда мысленно порадовался за автора совета, это был известный и авторитетный чел, но лично я сделал бы совершенно по-другому, напишу об этом позже.

Скрипты нужны совсем для другого: например, у меня есть скрипт, который по очереди логинится (сам!) на каждый тестовый сервер (у нас их много) и ищет там инсталляции разных версий продукта, записывает в список адреса web-интерфейсов и баз данных, на которые они настроены. Скрипты позволяют автоматизировать большое количество рутинных действий, и именно для этого их применяют. Поэтому меня передёргивает, когда я вижу скрипты, подобные описанному выше — это напрасно занятые 512 байт дискового пространства. Хороший скрипт должен иметь все шансы стать самостоятельной программой. Скрипты можно писать на любом языке программирования, лишь бы не тошнило и складывать в любые места, лишь бы не мешались. Некоторым нравится прописывать директории со скриптами в переменную $PATH, чтобы не ходить за ними каждый раз, когда понадобится, но лично я считаю, что это лучший подход: скрипты автоматизируют действия локального значения и глобальный доступ к ним практически не нужен.


4. Makefile. А вот это самый непопулярный способ, но на самом деле он очень удобный. Я помню, как впервые встретил в сети совет использовать Makefile для автоматизации некоторых рутинных операций (но уже не помню, каких именно), и помню, как тогда удивился: "нафига, если можно написать просто на шелле?!". Теперь я знаю, что Makefile — это программа, которая неплохо отслеживает зависимости, имеет возможность проводить любые действия при определённых обстоятельствах и, что самое главное, не делать их, если нет необходимости их делать. Причём необходимость определяет сам интерпретатор, главное — дать ему правильные критерии.

Например, у меня есть Makefile, который автоматически обновляет инсталляцию из svn (прописана цель svn_up) и если обновился файл alter.txt (в него программист пишет изменения, которые нужно внести в БД), то изменения автоматически вносятся в базу. Суть в том, что make делает действия, описанные в цели, только в том случае, когда целевой файл устарел. Поэтому, например, бэкап файла нужный_файл.тхт делается вот таким Makefile:
/var/tmp/backup:
        mkdir -p /var/tmp/backup
/var/tmp/backup/needful.txt.backup: needful.txt /var/tmp/backup
        cp needful.txt /var/tmp/backup/needful.txt.backup
backup: /var/tmp/backup/needful.txt.backup

Чтобы понять, почему в данном случае Makefile удобен, посмотрим, как он работает.
 $ touch needful.txt 
 $ make backup
mkdir /var/tmp/backup
cp needful.txt /var/tmp/backup/needful.txt.backup
 $ make backup
make: Цель `backup' не требует выполнения команд.
 $ touch needful.txt 
 $ make backup
cp needful.txt /var/tmp/backup/needful.txt.backup
Как легко можно заметить, make сам проверяет, обновились ли файлы, нужно ли создать каталог и т.п. В проектах для сборки программного обеспечения Makefile'ы с целями для каждого объектного файла и для каждого бинарника генерируют специальные тулзы. Нам же нужно описать только небольшое количество действий, поэтому мейкфайл оказывается не таким уж страшным и непонятным.

Конечно, в данном конкретном случае можно использовать и rsync, и tar, и вообще пример синтетический, но удобство такого способа автоматизации показывает наглядно. В более сложных случаях, правда, приходится вводить "сигнальные" файлы, то есть, если цель не является файлом, то запомнить время её последнего выполнения можно с помощью одноимённого файла, спрятанного в специально выделенном подкаталоге.

Команда make по умолчанию ищет файл Makefile в текущем каталоге, поэтому можно создавать разные программы в разных местах и не бояться, что они будут путаться.

_______________

Теперь у меня есть систематизированное представление о том, в каком случае какие способы можно использоваться для автоматизации простой рутины. Для сложной рутины народ пишет программы, создаёт системы мониторинга и оповещения и использует VBscript. Нас это совершенно не интересует ©, потому что мы работаем в другой области. Единственное, что хочу добавить: чудо-алиасы и шелл-функции позволили мне отказаться от такого DOS'овского наследия, как MidnightCommander, на пользователей которого я теперь смотрю с подозрением.

1 комментарий:

  1. Этот комментарий был удален администратором блога.

    ОтветитьУдалить