Пока я работал с Неназываемым — столкнулся с необходимостью многократно прогонять комплект действий с питоновскими скриптами. Потратив примерно полчаса (чуть меньше на самом деле — учитывайте, что я не профессиональный программист, а скромный power user) разобрался в GNU make и написал себе такой вот файлик:
all: struc func
clean:
rm *.csv
rm *.dot
rm *.png
struc:
python st.py
find . -name '*.dot' -exec dot {} -Tpng -o {}.png \;
func:
python fun.py
find . -name '*.dot' -exec dot {} -Tpng -o {}.png \;
one:
python st.py
dot struct_kond_B1_01.dot -Tpng -o poligon.png
geeqie poligon.png
В данном случае make struc
считает одни аспекты Неназываемого и с помощью graphviz
(точнее dot
) рисует к ним диаграммы, make func
делает тоже самое для других аспектов Неназываемого, make one
— отладочный «единичный сценарий», make clean
чистит папку от лишних файлов (храните в версионном контроле только то, что редактируется руками, ага). make
или make all
прогоняет сценарии всех аспектов Неназываемого.
Удобно. Когда освоил — понял, что именно make
мне не хватало для автоматической обработки данных. Вообще говоря make
предназначен совсем-совсем для другого, но с коммандлайновыми утилитами он прекрасно автоматизирует действия и в тоже время делает это очень прозрачно, избавляя от необходимости плодить где-то по окрестностям скрипты и алиасы.
Например.
Подкасты у меня качаются через newsbeuter
(точнее через podbeuter
) попадают в ~/av/pod
, откуда через unison
синхронизируются с папкой pod
в плейере. Я люблю, чтобы подкасты лежали по папкам — на случай, если у меня будет настроение слушать что-то конкретное. Но. С другой стороны иногда хочется слушать их «в порядке поступления» — отсортированными по дате. Для чего достаточно иметь плейлист.
В ~/av/pod/
кладем makefile
следующего содержания:
# Генерация плейлистов в прямом и обратном порядке дат для плейера
all: m3u back
m3u:
echo '#EXTM3U' > aktual.m3u8
find . -name "*.mp3" -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //' | head -n 50 >> aktual.m3u8
back:
echo '#EXTM3U' > back.m3u8
find . -name "*.mp3" -type f -printf '%T@ %p\n' | sort -k 1n | sed 's/^[^ ]* //' | tail -n 10 >> back.m3u8
Теперь по make
в папке будут созданы два файла aktual.m3u8
и back.m3u8
в которых файлы перечислены «в порядке поступления». У меня забит такой алиас:
alias podbeuter='podnorm && podbeuter && make -C ~/av/pod'
podnorm
— это предобработка очереди закачки, о которой речь пойдет в другой раз. Видно, что каждый раз когда я вызываю podbeuter, после выхода из него выполняется makefile
для ~/av/pod
, который генерит нужные плейлисты. А они уже автоматом через unison
попадают в плейер.
Еще один пример. Иногда мне удобно комитить и пушить все репозитории, которые у меня есть — я использую битбакет, как бэкап и историю.
Раньше у меня в ~/bin/
лежали монструозного вида hgci
и hgpush
, которые проходили по папкам и выполняли все требуемое. Теперь в корневике лежит Makefile
такого вида:
# Посмотреть, что есть
show:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \;
# Commites
ci:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg ci -m 'autocommit by makefile' \;
commit:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg commit -m 'autocommit by makefile' \;
ak:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg ak \;
# Push
push:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg push \;
# Pull
pull:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg pull \;
# Update
up:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg up \;
update:
find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir pwd \; -execdir hg update \;
Следующий пример. Я храню различные rc
-файлы в ~/bin/rc
, причем основные соответствующие им файлы завязаны на оные через soft-линки.
Например:
vik@firefly:~$ ls -al ~/.config/openbox/
lrwxrwxrwx 1 vik vik 37 окт 30 00:36 autostart.sh -> /home/vik/bin/rc/openbox/autostart.sh
lrwxrwxrwx 1 vik vik 33 окт 30 00:36 menu.xml -> /home/vik/bin/rc/openbox/menu.xml
lrwxrwxrwx 1 vik vik 31 окт 30 00:36 rc.xml -> /home/vik/bin/rc/openbox/rc.xml
lrwxrwxrwx 1 vik vik 37 окт 30 00:36 rootmenu.xml -> /home/vik/bin/rc/openbox/rootmenu.xml
~/bin
, как я писал выше, синхронизируется с битбакетом. Чем это удобно? Любое изменение в управляющих rc-файлах попадает под контроль версий и синхронизируется с прочими компьютерами. Таким образом у меня не просто стандартно обеспечивается одинаковое рабочее место на всех моих машинах, но и одинаковое изменение, если я где-то что-то меняю.
Такая система хороша всем, за исключением того, что при первой инсталляции требуется ручками прописать везде эти софт-линки. Но зачем делать это ручками?
vik@firefly:~$ cat ~/bin/rc/Makefile
all: pod main openbox
pod:
ln -s --backup ~/bin/rc/makepod/makefile -t ~/av/pod
main:
ln -s --backup ~/bin/rc/all_hg_Makefile/Makefile -t ~
openbox:
ln -s --backup ~/bin/rc/openbox/* -t ~/.config/openbox/
Makefile
на самом деле больше, но идея, думаю тоже ясна.
Ну и на закуску. Методички, книги и прочая литература обычно лежат каждая в своей папке, в которой book.txt
— это сам текст, а makefile
выглядит примерно так:
# makefile для конверсии из markdown в разные форматы и быстрого просмотра комментариев
# предполагается что директория под контролем hg (и версии извлекаются оттуда)
# комменты отбиваются либо через %% в начале строки либо выделены двойными %% %% в тексте
# в системе должны быть pandoc, hg, sed, head и grep
# комментаторство
com: outline inline
# внутренние комментарии
inline: book.txt
grep '\(%%.\+%%\)' book.txt -n --color | sed 's/^/book.txt:/'
# внешние комментарии
outline: book.txt
grep '^%%.\+' book.txt -n --color| sed 's/^/book.txt:/'
# конверсия
# получаем версию книги
rev:
echo "\tслужебная информация:" > rev.ed.txt
hg log -f book.txt | sed -n -e '1,4s/^/\t/p' >> rev.ed.txt
# основа md-файла без комментов и с ревизией в голове
txt: rev
sed '/^%%/ d' book.txt > book.ed.txt
echo '***' >> book.ed.txt
cat rev.ed.txt >> book.ed.txt
# чистый md-файл с заэкранированными диалогами
md: txt
sed 's/^-[ ]/\\- /g' book.ed.txt > book.ed.ed.txt
# html-файл
html: md
pandoc -f markdown -t html --standalone book.ed.ed.txt -o book.ed.html
finehtml: md
sed 's/^\\- /--- /g' book.ed.ed.txt > book.fine.ed.txt
sed 's/ - / --- /g' book.fine.ed.txt > book.ed.ed.txt
cp book.ed.ed.txt book.fine.ed.txt
pandoc --smart -f markdown -t html --standalone book.fine.ed.txt -o book.ed.html
# html-файл с содержанием
t_html: md
pandoc -f markdown -t html --toc --standalone book.ed.ed.txt -o book.ed.html
odt: md finehtml
libreoffice4.0 --headless --convert-to odt book.ed.html
doc: md finehtml
libreoffice4.0 --headless --convert-to doc book.ed.html
pdf: md finehtml
libreoffice4.0 --headless --convert-to pdf book.ed.html
epub: md
pandoc -f markdown -t epub --toc --standalone book.ed.ed.txt -o book.ed.epub
# очистка от мусора
clean:
rm -f *.ed.odt *.ed.txt *.ed.html *.ed.rtf *.ed.doc *.ed.epub *.ed.pdf
Почему make, а не что-то более сложное? Потому, что make есть (и работает везде) и потому, что make с одной стороны достаточен для подобных задач, с другой прозрачен в использовании (в каждой папке с данными лежит свой Makefile, который выполняет совершенно определенные задачи и который всегда можно просмотреть на предмет подсказки).
Почему make, а не комплект скриптов? Опять-таки из-за прозрачности с одной стороны, из-за емкости с другой стороны (в файл умещается очень много задач, кроме того задачи можно исполнять «каскадами», завязывая их друг на друга) и из-за стандарта с третьей стороны (не нужно ничего придумывать — все уже готовое и оптимальное.
Словом, пытливому уму есть, где развернуться.