Введение
Машины всегда будут быстрее, независимо от того насколько мы продуктивны и как быстро мы набираем команды. Суровая правда жизни. С другой стороны, если мы выполняем одно и тоже действие множество раз, то почему бы не заставить машины страдать. Написать скрипт на
bash
(ваш любимый язык программирования) и каждый раз вызывать этот скрипт, а не набирать монотонные команды, которые забирают так много времени, сил и энергии. А мы, пока скрипт будет выполнять свою работу, можем помечтать о том, как космические корабли бороздят просторы нашей Вселенной.В прошлой статье мы рассмотрели основы программирования на
bash
. Сегодня мы будем применять полученные знания на практике.План автоматизации
- Быстрый
diff
- Быстрый
diff
+ Jira API - Очистка
_dist
- Up большого числа репозиториев
- Клонирование большого числа репозиториев
- Полезные алиасы
В этот список попали задачи, которые я выполняю ежедневно несколько раз в день, а то и в час. Вообще, автоматизация рутинных процессов — это творческий и сугубо личный процесс.
Автоматизировать можно все, что вы только можете придумать. Надеюсь, что в конце статьи у вас появится свой план по автоматизации и вы заставите машины страдать. Сделайте себе чашечку ароматного кофе и усаживайтесь поудобней. Вас ждет увлекательное путешествие в мир автоматизации средствами
bash
.Быстрый diff
В своей работе на проекте мы используем Git. Создание
diff
является довольно частой задачей. Чтобы создать diff
для конкретной ветки нужно выполнить следующую команду:git diff origin/master origin/<branch-name> > "${HOME}/diff/diff-<branch-name>.diff"
<branch-name>
— имя ветки для которой нужно создать diff
Недостатки данного подхода
- Каждый раз команду нужно набирать вручную
- Большая вероятность ошибки в процессе набора
- Сложно запомнить
Эти недостатки можно с легкостью решить при помощи
bash
. В идеале все должно работать так:- Набрали команду
- Передали ей имя ветки
- Получили
diff
Финальный вид команды
gdd <branch-name>
Автоматизируем
Теперь вместо длинной строки команд, достаточно набрать на клавиатуре
./fast_diff.sh <branch-name>
. Если забыли указать имя ветки, нам об этом любезно сообщит данный скрипт.Финальный штрих
Стоп, скажете вы. А как же финальный вид команды. Ведь в текущем виде данный скрипт не очень удобен, мы привязаны к директории в которой он находится.
Рассмотрим более подробно как сделать для исполняемого файла отдельную команду, а не писать к нему каждый раз относительный/абсолютный путь.
У каждого пользователя в его домашней директории (
~
) располагается поддиректория bin
. Если такой нет, то ее можно создать. В ней могут хранится исполняемые файлы. Удобство заключается в том, что такие файлы доступны по имени и к ним не нужно указывать относительный/абсолютный путь. В эту директорию я поместил файл gdd
, который отвечает за создание diff
:#!/bin/bash
"${HOME}/htdocs/rybka/tools/fast_diff.sh" "$@"
Несколько важных моментов:
- У файла не принято указывать расширение.
- Для файла нужно явно указать атрибут
x (chmod +x <filename>)
.
- Если директория
bin
не находится в переменной$PATH
, нужно добавить ее явноPATH="${PATH}:${HOME}/bin"
.
Для того, чтобы этот файл стал доступным, перезапустите терминал. Теперь, чтобы создать
diff
, достаточно выполнить следующую команду:gdd <branch-name>
Не очень удобно все время создавать отдельный файл в директории
bin
для каждой команды. Мы можем это оптимизировать воспользовавшись символическими ссылками:ln -s "${HOME}/htdocs/rybka/tools/fast_diff.sh" gdd
Быстрый diff + Jira API
Если вы используете в своей работе менеджер задач Jira или любой другой менеджер задач, который предоставляет API, то можно пойти еще дальше. К примеру, можно с помощью Jira API прикреплять
diff
к конкретной задаче. Для этого нам понадобится еще и cURL.Алгоритм решения
- Вызываем скрипт
- Передаем
id
задачи - Если
id
задачи не передано, выводим сообщение пользователю - Если все верно, генерируем
diff
и прикрепляем его к задаче
Финальный вид команды
gdd_jira <issue_id>
Автоматизируем
Как вы могли заметить, имя ветки не нужно указывать. Ее мы получаем при помощи нехитрых манипуляций с командами git:
branch=$(git rev-parse --abbrev-ref HEAD)
Очистка _dist
Для начала, давайте разберемся за что отвечает директория
_dist
. Это место, куда попадают файлы CSS
, JavaScript
, всевозможные шаблоны (Jade/Pug, Handlebars, др.) и прочие файлы после запуска системы сборки (Grunt, Gulp, др.). Эта директория не обязательно должна называться _dist
. Возможны вариации.Для одного из проектов мы используем Grunt. Довольно часто наша команда сталкивается с проблемой, что Grunt не всегда замечает изменения в некоторых файлах (проблема в основном, с Less файлами). Чтобы исправить эту ситуацию нужно очистить директорию
_dist
для одной из тем или для всех тем сразу. Да, данную задачу можно решить и с помощью Grunt. Даже можно все время удалять данную директорию вручную. Но это будет не так эффективно и удобно, как в случае с bash
. Количество этих директорий (_dist
) не одна и не две, и даже не десять или двадцать. Их много. Основное требование к скрипту — не применять лишние обертки и/или зависимости без необходимости.Рассмотрим вариант без применения
bash
. Используем всю мощь оболочки для решения этой задачи:find <path-to-themes> -type d -name "_dist" | xargs rm -rfv
<path-to-themes>
— путь к директории, где находятся все темыНедостатки у данного подхода примерно те же, что я приводил и в случае с задачей по созданию
diff
. Плюс ко всему в данном случае нет возможности указывать для какой конкретно темы нужно удалить директорию _dist
.Алгоритм решения
- Вызываем скрипт
- Если название темы не было передано, удаляем директорию
_dist
во всех темах - Если имя темы было передано, выполняем удаление директории
_dist
в конкретной теме
Финальный вид команды
clean_dist [<theme_name>]
Автоматизируем
Up большого числа репозиториев
Представьте, что вы работаете с большим проектом. В этом проекте есть директория, отведенная под сторонние репозитории, которые вы не разрабатываете, но поддерживать их в актуальном состоянии обязаны. Конечно, если этих репозиториев два-три, то это не такая большая проблема. Хотя и тут я бы поспорил. А если у вас таких репозиториев 10-15, и эта цифра постоянно растет. В результате вы не успеваете за ними следить или тратите несоизмеримо много времени на поддержку. Почему бы эту задачу не автоматизировать.
Алгоритм решения
- Перейти в директорию с репозиторием
- Проверить, чтобы репозиторий был на ветке
master
- Если репозиторий не на ветке
master
, сделатьgit checkout
- Сделать
git pull
Важный момент. Даже, если репозиторий находится на ветке
master
, мы не можем быть уверены, что он в актуальном состоянии. Исходя из этого, делаем git pull
в любом случае.Финальный вид команды
up_repo
Автоматизируем
Клонирование большого числа репозиториев
Данная задача тесно связана с предыдущим пунктом автоматизации. Чтобы конечный пользователь имел возможность воспользоваться предыдущей командой на практике, необходимо предоставить набор репозиториев сторонних разработчиков, которые будут располагаться в директории
bash/core/vendors
, и о которых пользователю, по большому счету, не нужно ничего знать. По аналогии с npm модулями этот набор репозиториев не должен поставляться вместе с основным репозиторием. Все что нужно сделать пользователю — это выполнить команду и дождаться завершения клонирования репозиториев.Алгоритм решения
- Список репозиториев задан в виде массива
- Запускаем цикл по этому массиву
- Уделяем особое внимание, если один вендор имеет больше одного репозитория
- Выполняем дополнительные проверки
- Выполняем
git clone
Финальный вид команды
clone_repo
Автоматизируем
Полезные алиасы
У меня есть несколько вопросов к читателям. Вы должны ответить честно самому себе. Как часто вы используете эту команду?
git branch
А эту команду?
git status
А вот эту команду?
git push origin <branch-name>
Как насчет этой команды?
ps aux | grep <user-name>
Все верно, этот список можно продолжать бесконечно и он у каждого свой. И тут, неожиданно, к нам приходит озарение:
Правильно. Для команд, которые вы используете часто — создавайте алиасы. Вот лишь небольшой список тех алиасов, что я использую:
Чтобы проверить какие алиасы у вас заданы, достаточно выполнить команду
alias
без параметров.Куда поместить алиасы
В большинстве случаев для таких целей используют файл
.bashrc
, который располагается в домашней директории пользователя. Еще есть файл под названием .gitconfig
, в который можно добавлять алиасы для работы с git
.Не стоит менять алиасы поздно ночью
Алиасы — это мощный инструмент. Только тут как с паролями. Не стоит менять алиасы поздно ночью. В одну прекрасную ночь, я поменял один из алиасов и забыл. На следующий день я потратил полдня, разбираясь почему ничего не работает.
Вместо заключения
Как только я начал разбираться с основами программирования на
bash
, первая мысль, которая меня посетила: «Стоп, это же нужно больше для системных администраторов...». Но в то же время, я понимал, что мне нужны эти знания, чтобы хоть как-то избавить себя от ежедневных рутинных задач. Сейчас я могу с уверенностью сказать, что эти знания нужны не только системным администраторам. Они пригодятся всем, кто хоть как-то взаимодействует с удаленным сервером или работает на OS *nix
подобных системах. Для пользователей, которые работают на Windows OS эти знания тоже пригодятся (Bash on Ubuntu on Windows, Windows and Ubuntu Interoperability). В простейшем случае, скрипт — это ни что иное, как простой список команд системы, записанный в файл. Такой файл может облегчить вам рабочие будни и избавит от необходимости выполнять рутинные задачи вручную.Полезные ссылки по некоторым возможностям
bash
, которые были использованы в примерах:- Перенаправление ввода/вывода
- Функции
- Массивы
- Двойные круглые скобки
- Объединение команд в цепочки (конвейер)
- Завершение и код завершения
- Псевдонимы
- Как правильно добавлять пути в переменную $PATH
На этом все. Спасибо за внимание. Кто дочитал до конца, отдельное спасибо.
Комментарии (30)
alexyr
20.02.2017 10:12+32. Если название темы не было передано, удаляем директорию _dist во всех темах
А это опасно! Лучше прописать флаг, типа --all, для удаления всего. А на отсутствие аргументов вывести help. Иначе можно случайно удалить всё, не желая того… Возможно в конкретно этом случае ничего страшного, но деструктивные действия без подтверждения это «фу-фу-фу»var_bin
20.02.2017 11:51Полностью согласен с вами, что это опасно ) И вы правильно заметили по поводу флага, типа
--all
и help. Почти так оно сейчас и работает. На хотел перегружать пример лишним кодом. Мне хотелось, чтобы читатель заметил, где можно сделать лучше и что можно еще больше оптимизировать, защитить и т.д. Вы заметили — значит все круто.
tytar
20.02.2017 12:15ИМХО, у алиасов есть две стороны медали — с одной стороны удобно, очень удобно. А с другой, если ты работаешь только на своей машине то, в принципе, другой стороны нет. Но, если периодически приходиться работать с удаленными, то любое gf, gm и т.д. выливаються небольшую, совсем маленькую, но очень бесящую «боль».
var_bin
20.02.2017 12:20Добрый день!
Да, согласен, есть такое. Но если у вас есть права на запись в файл
.bashrc
и на удаленном сервере все навтроено верно (каждый пользователь со своими настройками), то не составляет труда перенести туда свои алиасы. Я так и сделал. И не испытываю неудобствtytar
20.02.2017 12:22Это пока Вы не поднимаете пачками инстансы, и когда нуджно каждый по своему настраивать. ДевОпс, такой ДевОпс.
thunderspb
20.02.2017 13:32ну пользователя тоже можно запровиженить налету :) можно ансиблом донастроить своего пользователя
ZyXI
20.02.2017 12:21У каждого пользователя в его домашней директории (~) располагается поддиректория bin. Если такой нет, то ее можно создать. В ней могут хранится исполняемые файлы. Удобство заключается в том, что такие файлы доступны по имени и к ним не нужно указывать относительный/абсолютный путь. В эту директорию я поместил файл gdd, который отвечает за создание diff:
Впервые слышу про ~/bin: у меня он есть, но в $path я этот каталог добавлял сам в ~/.zshrc. Как и ~/.local/bin (туда pip кладёт; может и ещё кто — каталог выглядит более стандартным, к тому же ~/.local/share является значением по?умолчанию в XDG Base Directory Specification), ~/.luarocks/bin и ~/.gem/ruby/*/bin.
И зачем делать скрипт, вызывающий другой скрипт, есть же символические ссылки?
ZyXI
20.02.2017 12:25Для того, чтобы этот файл стал доступным, перезапустите терминал. Теперь, чтобы создать diff, достаточно выполнить следующую команду:
А перезапускать?то зачем? Если ~/bin уже был в $PATH, то оно сразу работать начнёт, если нет — после того, как добавите. В zsh ещё нужно написать
rehash
: без него тоже работает, но автодополнения берёт данные из кэша, который не обновляется автоматически.
var_bin
20.02.2017 12:35-1Добрый день, Николай.
Впервые слышу про ~/bin:
Привык пользоваться этой директорией на удаленном сервере. По поводу остальных, что вы назвали затрудняюсь ответить. Подозреваю, что некоторые из них могут быть ограничены по правам.
И зачем делать скрипт, вызывающий другой скрипт, есть же символические ссылки?
Хм, интересный вопрос. С такой ссылкой останется тот же функционал, что я описал в статье?
ZyXI
20.02.2017 12:48+1Хм, интересный вопрос. С такой ссылкой останется тот же функционал, что я описал в статье?
Да, bash не проверяет, символическая ссылка или нет, он проверяет только можно ли выполнить файл через stat(), а stat() stat’ит файлы по ссылке. Zsh делает больше системных вызовов и других, но тоже не обрабатывает символические ссылки специальным образом. У меня ~/bin – символическая ссылка и больше половины файлов внутри символические ссылки и это работает.
var_bin
20.02.2017 13:09Да, вы правы, все работает. Только что перепроверил. Спасибо.
Добавлю этот пункт в статью
ZyXI
20.02.2017 12:56Привык пользоваться этой директорией на удаленном сервере. По поводу остальных, что вы назвали затрудняюсь ответить. Подозреваю, что некоторые из них могут быть ограничены по правам.
Как ограничены? Либо из каталога можно читать файлы и их можно тогда выполнить, либо нет. Единственный вариант, что мне приходит в голову — монтировать с noexec, но кто и зачем будет это делать с ~/.local? Я бы не удивился, увидев на каком?нибудь компьютере /home с noexec — требования по безопасности бывают разные, — но тогда и ~/bin не поможет (если только он не символическая ссылка куда?то без noexec).
sabio
20.02.2017 18:56Алиасы для Git, кстати, можно задавать в настройках самого Git.
Некоторые интересные идеи есть, например, вот в этой статье — Human Git Aliases
P.S. Ваш алиас 'gbd' весьма опасен. Захотите вы запустить отладчик, а вместо этого нечаянно удалите ветку (да ещё и с -D)
var_bin
20.02.2017 20:29Добрый день, Дмитрий.
Алиасы для Git, кстати, можно задавать в настройках самого Git.
Упоминал про это в статье "Еще есть файл под названием
.gitconfig
, в который можно добавлять алиасы для работы сgit
.". Не стал расписывать, чтобы не перегружать статью.
P.S. Ваш алиас 'gbd' весьма опасен. Захотите вы запустить отладчик, а вместо этого нечаянно удалите ветку (да ещё и с -D)
Пока живем с ним мирно =)
Denkenmacht
20.02.2017 20:04+1подобных системах. Для пользователей, которые работают на Windows OS эти знания тоже пригодятся
Позволю себе высказать предположение, что пользователи, работающие на Windows, вряд ли будут заниматься предложенными вами по ссылкамизвращениямиразвлечениями, а продолжат юзать VBScript.ZyXI
20.02.2017 21:00Одна из первых вещей, которые я делаю на Windows, куда я могу ставить ПО — это установка babun (cygwin + пачка программ, включая zsh и эмулятор терминала).
Dreyk
21.02.2017 16:18я настороженно отношусь к введению такого количества коротких алиасов. как уже сказали выше рано или поздно оно законфликтует с каким-нибудь бинарником
гораздо лучше сохранить ваш скрипт fastdiff в файл
git-fastdiff
, который точно так же положить в$PATH
и потом вызывать его как
$ git fastdiff <branch-name>
var_bin
21.02.2017 17:48Добрый день.
Все возможно. Этими алиасами пользуюсь уже больше трех лет и никаких конфликтов ). Ваш подход тоже рабочий и очень даже практичный. Но немного больше настроек.
alexyr
Откуда такая информация? Всё прекрасно работает из bin с расширениями! Красивее — это да…
И вообще, как вариант, в файл gdd можно положить весь код из fast_diff.sh, а не вызов самого fast_diff.sh
var_bin
Добрый день, Алексей.
Да, вы правы, работает с расширением и можно весь код запихнуть в этот файл. Без расширения, чтобы обращение к файлу выглядело как команда, а не выводило мне всякий раз расширение. По поводу положить весь код в файл
gdd
. Не делаю так из соображений организации кода. Все подобные файлы располагаются в директорииtools
, а те файлы которые отвечают за их вызов в директорииbin
. Плюс ко всему, директорияtools
находится под git'ом.ZyXI
Здесь логичнее было бы написать «не принято указывать». «Не должно быть» неверно.