bash-скриптинг (сборник рецептов)

Перевод книги по написанию сценариев на BASH

Надоело смотреть историю bash - там нет пояснений - что и для чего использовалось. Поэтому решил создать данную страничку.

Тут я и буду располагать рецепты, которые по моему мнению стоит запомнить.

Создание толпы виртуальных хостов для апача по шаблону из списка

Есть список хостов list.txt , копируем файл виртуалхоста site.sbm-bio.net в файлы новых хостов, затем односторочным скриптом заменяем внутри этих файлов значения директив DocumentRoot ServerName и ServerAlias на нужные имена, а вместе с этим создаем директории DocumentRoot и кладем в них index.php с содержанием «working».

cd /etc/apache2/sites-available
 
cat list.txt |awk '{print "cp site.sbm-bio.net "$1}'|bash
 
cat list.txt |while read i; do \
           echo "Обрабатываем: $i"; \
           echo "sed  -i 's/site.sbm-bio.net/$i/g; s/sbm-group.ru/$i/g' $i ; 
                  mkdir -p /var/www/$i/public_html ; 
                  echo working>/var/www/$i/public_html/index.php "|bash; \
   done;

Описание всех таблиц в БД MySQL

Приспичило вытащить навание всех полей для каждой таблицы в одной из баз MySQL.

Написал однострочную портянку, которая сделала всю работу.

Вот она:

 root # echo "show tables;"| \
   mysql -u root --password=HACKME dbname  |  \ 
   grep -v Tables_in_ | \
   while read i; \
       do \
         echo  ====$i==== ; \
         echo "describe \`$i\` ;" | \   
         mysql -u root --password=HACKME dbname ; \
         echo "==================="; \ 
         echo; echo; \
       done | \
grep -v Field |   awk '{print $1}'                  

Записывал конечно все это без переносов - в одну строку. Вот оригинал:

echo "show tables;"|mysql -u root --password=HACKME dbname  | grep -v Tables_in_ | while read i; do echo  ====$i==== ; echo "describe \`$i\` ;" |mysql -u root --password=HACKME dbname ; echo "==================="; echo; echo; done |grep -v Field |awk '{print $1}'

Видео для публикации в веб

Встала задачка конвертировать видео-файлы в формат FLV. Тут лучшим инструментом является mencoder, поставляемый с mplayer. Компетентные люди сказали, что брать mplayer лучше всего из svn. Взял из trunc/ - собиралось конечно не без проблем - в моем любимом Debian пришлось поколдовать с установкой библиотек-dev. В общем собрал, но man mencoder читать было пока некогда - выкладываю тут строку, полученную от компетентных лиц :). Уже проверил - реально работает.

/usr/local/bin/mencoder Mov.wmv -o vvo.flv -of lavf \
-oac mp3lame -lameopts cbr:br=64 -af lavcresample=22050 \
-ovc vfw -xvfwopts codec=vp6vfw.dll \
-vf yadif,scale=640:480,flip \
-ss 000:00:15 -endpos 000:00:18 

-ss и -endpos нужны только если вы собираетесь образать ролик - это соответственно смещение начала среза и его конца.

Если вы собираетесь пускать FLV потоком через nginx или php-скрипт - придется расставить ключевые кадры.

В этом поможет ruby-скрипт flvtool2

flvtool2 -UP rolik.flv

Все проверено - стриминг видео пашет!

Удаление ненужных строк из файла

Есть файл в котором в каждой строке должно быть 4 слова. Те строки где больше слов - нужно удалить!

Сначала неправильный вариант :-)

cat 16.07.mail |awk '{if(NF>4){print "sed -i \"" NR " d\"  16.07.mail";}}'|bash

После первого прохода номера строк сдвигаются и в следующие проходы удаляются нужные строки.

Исправим ситуацию:

while true; do cat 16.07.mail |awk '{if(NF>4){print NR;}}'|head -n 1|awk '{print  "echo " $1"; sed -i \"" $1 " d\"  16.07.mail"}'|bash ; done

Работает такая штука безумно долго, но зато надежно!

Банальная защита от DDoS

tail -n 300 access.log|grep "GET / "|awk '{a[$1]++;} END{for( i in a){ if(a[i]>5){ print i;}}}' | awk -F '.' '{print $1"."$2"."$3".0/24"}'|while read i; do iptables -I INPUT -s $i -j DROP; echo $i;   done;

Подсчет количества коммитов в Subversion

Подсчитаем кол-во коммитов каждого коммитера в некотором репозитории Subversion:

svn log -r 0:HEAD|grep line|awk -F '|' '{a[$2]++} END{for(i in a){print a[i]” “i;}}’|sort -nr

скрипт выдаст список в два столбца - в первом кол-во коммитов, а во втором логин участника. Список сортируется по количеству коммитов по убыванию.

Пакетное изменение слова в толпе файлов

Поменял название поля в БД - встала задача поменять поле везде где используется его имя. Используется оно у меня в конфигурационных файлах и в шаблонах. Задача усугубляется еще и тем что все дерево каталогов лежит под SVN - а файлы в каталогах .svn трогать нельзя. Чешем репу… Эксперементируем - вот что получается:

Изменяем шаблоны:

grep -rn 'sort' templates|grep -v svn|awk -F ':' '{print $1}'|sort|uniq|while read i; do sed -i 's/sort/sorting/g' $i ; done

Изменяем конфиги:

 sed -i 's/\[sort\]/[sorting]/g; /\[sorting\]/ a name="Сортировка" ; ' *.ini

Конфиги - это обычный двух-уровневый ini-файл. Задача была изменить имена уровней sort на sorting , и еще добавить к каждому из них параметр name=«Сортировка»

Эх…. и что бы я делал с такими задачами в винде???

Массовая конвертация изображений

Попросил меня друг написать скриптик, чтобы ресайзить все картинки из папки, перемещая при этом уменьшенные копии в другую папку.

Вот как это было:

find icons -exec file {} \;
icons: directory
icons/font_2.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
icons/hdd_mount1.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
icons/user_female.png: PNG image data, 64 x 64, 8-bit/color RGBA, non-interlaced
icons/ascii.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
icons/kfloppy_1.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
icons/html.svgz: gzip compressed data, from Unix
icons/folder_yellow.svgz: gzip compressed data, from NTFS filesystem (NT)
icons/kfloppy_2.png: PNG image data, 64 x 64, 8-bit/color RGBA, non-interlaced
icons/amor.png: PNG image data, 64 x 64, 8-bit/color RGBA, non-interlaced
icons/font_bitmap.svgz: gzip compressed data, from Unix

будем уменьшать на 24 сначала отыскать все файлы. читать их в переменную

max@maximus:~/tmp$ find icons/ -type f |while read i; do echo $i; done
icons/font_2.png
icons/hdd_mount1.png
icons/user_female.png
icons/ascii.png
icons/kfloppy_1.png
icons/kfloppy_2.png
icons/amor.png
icons/mime_empty.avcs.png
icons/image_3.png
icons/colorscm_1.png
icons/hd2-black.png
icons/folder_cyan.png
icons/image_4.png
icons/folder_html111.png
icons/source_h111.png
icons/kchart_1.png
max@maximus:~/tmp$    

чтобы копировать нам поможет вот что:

         
max@maximus:~/tmp$ basename t/e/w/r.sl
r.sl

Далее мы хотим ресайзить картинки

max@maximus:~/tmp$ convert --help |grep resize
  -resize geometry     resize the image
  -support factor      resize support: > 1.0 is blurry, < 1.0 is sharp
max@maximus:~/tmp$                                                             

вставляем это в скрипт

max@maximus:~/tmp$ find icons/ -type f |while read i; do echo convert -resize 24X24 $i final/`basename $i`; done
convert -resize 24X24 icons/font_2.png final/font_2.png
convert -resize 24X24 icons/hdd_mount1.png final/hdd_mount1.png
convert -resize 24X24 icons/user_female.png final/user_female.png
convert -resize 24X24 icons/ascii.png final/ascii.png
convert -resize 24X24 icons/kfloppy_1.png final/kfloppy_1.png
convert -resize 24X24 icons/kfloppy_2.png final/kfloppy_2.png
convert -resize 24X24 icons/amor.png final/amor.png
convert -resize 24X24 icons/mime_empty.avcs.png final/mime_empty.avcs.png
convert -resize 24X24 icons/image_3.png final/image_3.png
convert -resize 24X24 icons/colorscm_1.png final/colorscm_1.png
convert -resize 24X24 icons/hd2-black.png final/hd2-black.png
convert -resize 24X24 icons/folder_cyan.png final/folder_cyan.png
convert -resize 24X24 icons/image_4.png final/image_4.png
convert -resize 24X24 icons/folder_html111.png final/folder_html111.png
convert -resize 24X24 icons/source_h111.png final/source_h111.png
convert -resize 24X24 icons/kchart_1.png final/kchart_1.png
max@maximus:~/tmp$                                                                                    

Остается передать вывод обратно на исполнение

find icons/ -type f |while read i; do echo convert -resize 24X24 $i final/`basename $i`; done |bash

Проверим что получилось

$ find final/ -type f -exec file {} \;
final/font_2.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/hdd_mount1.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/user_female.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/ascii.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/kfloppy_1.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/kfloppy_2.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/amor.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/mime_empty.avcs.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/image_3.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/colorscm_1.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/hd2-black.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/folder_cyan.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/image_4.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/folder_html111.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/source_h111.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced
final/kchart_1.png: PNG image data, 24 x 24, 8-bit/color RGBA, non-interlaced

можно конечно все делать в find через -exec или -xargs, но я обычно пишу однострочный скрипт потому что… 1) легче отлаживать . 2) до сих пор не знаю как ДВАЖДЫ подставить имя найденого файла в -exec

Отчет по журналу веб-сервера

Нужно было вынуть из журнала вебсервера все урлы начинающиеся с больших букв.

Как всегда я написал однострочный скриптик:

cat access.log | \
grep --colour=auto 'GET /[A-Z0-9][A-Z0-9][A-Z0-9]*'|\
gawk '{  key = gensub(/^.*(\/[A-Z0-9][A-Z0-9]+).*$/,"\\1","g",$0); a[key]++; } END{ for(i in a){print a[i]"\t"i;}}'|\
sort -nr
Последние изменения: %2010/%03/%22 %01:%Mar