BASH Tips&Tricks #0008: Голову с плеч!

  • BASH
Да-да, забудьте наконец об этой странной утилите, которая ещё и работает «как-то иначе» в некоторых менее популярных, чем Linux, реализациях *nix!
Почему даже одна head вам ни к чему, не говоря уже о двух и более?
Да потому что у нас есть sed, который умеет всё или почти всё, но остаётся при этом простым и чрезвычайно быстрым, что позволяет ему заменять целую тучу атомарных nix'овых утилит.
Ну вот, например…
Хотите первое поле 2-й строки CSV-файла, где разделителем служит (сюрприз!) — запятая?
Да нет ничего проще, чем сделать это одной командой, безо всяких глупых head'ов и медлительных awk, — вот:

sed -n '2{ s/,.*$//; p; q }' FILE

Да, и никогда не забывайте про жадный разбор регулярных выражений и фигурные скобки — данный пример наглядно показывает, чем бывает полезно и то, и другое.

Пожалуй, совсем не очевидным выглядит решение с помощью sed такой элементарной задачи, как «получить первые 5 строк файла»:

sed -n '1,5p; 6q' FILE

— с этим куда лучше справится head -5

Но, например, чуть менее элементарную задачу «вывести строки файла с 3-й по 10-ю, исключая 7-ю», sed решает куда нагляднее и элегантнее, нежели связка из доисторического локомотива head и «прицепного вагона» sed вместе:

sed -n '7b; 3,10p; 11q'


sed не умеет вести «обратный отсчёт» от конца файла, поэтому ему сложно соревноваться с tail в скорости и удобстве, но вот head он заменяет весьма успешно!
Поэтому в следующий раз, когда вы подумаете использовать sed на вагонной сцепке c head, подумайте ещё раз и сделайте «состав» немного короче…

BASH Tips&Tricks #0007: Организуем "многозадачное" сжатие

  • BASH
К сожалению, утилита xz до сих пор не умеет и уже, как видно, никогда не научится сжимать в несколько потоков (см. соотв комментарий в man-странице). 7z с некоторыми типами алгоритмов, в т.ч. с горячо любимым мной PPM, тоже не умеет распараллеливаться (во всяком случае ключ -mmt не даёт желаемого результата).
Но ведь можно осуществить сжатие нескольких файлов параллельно несколькими процессами! Единственная «хитрость» здесь будет заключаться в том, что для получения определённого гарантированного результата («всё сжалось»), нужно всё-таки подождать, чтобы все процессы завершились к моменту дальнейшей обработки файлов или к моменту, когда скрипт должен завершиться.

Всё это делается примерно в таком стиле:

for f; do
 xz $f &
 pids+=" $!"
done

for pid in $pids; do
 wait $pid
done


Время выполнения последнего цикла будет соответствовать времени выполнения самого «длительного» процесса сжатия, а для тех процессов, которые уже завершатся к моменту, когда до них дойдёт очередь «ожидания», команда wait просто отработает как NOP (no-operation в intel assembler :)).
Заметьте, что между двумя циклами можно вставить практически всё, что угодно, так что одновременно со сжатием можно делать и ещё что-нибудь полезное.

Простой загрузчик таблицы маршрутизации, он же "пример на массивы"

  • BASH
Да, я в курсе, что есть некие другие средства для того, чтобы делать тоже самое.
Тем не менее, есть у меня некое подозрение, что мой скрипт делает это наиболее наглядным, прямолинейным и безопасным способом.
Собственно, он даже и вовсе ничего не меняет, он ведь просто «печатает» команды, которые нужно выполнить для того, чтобы привести текущую таблицу маршрутизации в полное соответствие с сохранённой. Сами команды пишутся на STDOUT, редкие отладочные сообщения — на STDERR.
Читать дальше →

Простая "обёртка" для LVMSync

  • BASH
Написал незамысловатый фронтэнд для замечательной утилиты lvmsync, написанной Matt Shed. Спасибо тебе, Шэд, ты единственный человек, который удосужился разумным (т.е. читай: вменяемым) способом решить вопрос синхронизации блочных устройств. Да, только для LVM-томов. Но причина тому банальна до безобразия: LVM-тома — это пока единственный широко распространённый тип блочных устройств, изменения на которых можно отслеживать при посредстве самого механизма записи, а не выявлять каким-то хитрым образом уже пост-фактум, считывая произвольные куски, скурпулёзно высчитывая контрольные суммы и вообще занимаясь какой-то хиромантией. Есть конечно ddsnap от Zumastor'а, и он нашёл своё широкое применение на хранилищах свалок истории, предлагая патчить ядро Linux для решения «в общем виде» той задачи, которая в принципе такого решения иметь не должна.

Читать дальше →

BASH Tips&Tricks #0006: Как делать точно не следует

  • BASH
Наверное, это будет самый короткий пост в серии Tips&Tricks, потому что…

a="t*"
b="/etc/fs*"
c=/proc/1*

— да, вот именно так делать и не следует.
А именно: астериск (символ "*") в двойных кавычках и в присваивании переменной интерпретируется BASH не так, как вам хочется и даже не так, как хочется разработчикам BASH. Попробуйте и убедитесь сами.
Но лучше сразу экранируйте его, а для получения списка файлов пользуйтесь ls.

Всё!

BASH Tips&Tricks #0005: О том, что остаётся за скобками. За двойными.

  • BASH
BASH, сам по себе весьма мощный интерпретатор команд с очень широкими возможностями (в т.ч. и для написания кода, который не понимает даже его автор сразу после сохранения файла скрипта), становится просто кладезем интересных возможностей тогда, когда вы открываете для себя круглые, но в то же время такие многогранные, — (( двойные+скобки ))!

Дело в том, что выражения, заключенные в круглые двойные скобки, ведут себя так, словно их написали на одном из многочисленных диалектов языка Си (Perl-программисты, цыц, это не про вас!).
Вот, например, как преображаются циклы с использованием этой «super cow power» из мира си-подобных языков:

max_i=12
for ((i=1; i<=max_i; i++)); do
 date -d "2012-$i-01" +%B
done

Выражения, заключенные в «тёплые ладони» двойных круглых скобок помогают избавиться от уродливой конструкции ${} вокруг элементов массива. И вообще как только мы попадаем в чудесный мир Си-подобных кострукций за «двойным ограждением», у нас пропадает всякая необходимость постоянно писать $VAR или уж тем более ${VAR}. Узрите истину!
Читать дальше →

BASH Tips&Tricks #0004: Как получить "читабельный" LDIF-вывод

  • BASH
Что нам потребуется:
ldapsearch из поставки OpenLDAP 2.4.24, потому что начиная с этой версии появилась возможность отключить автоформатирование вывода с переносом строк благодаря опции ldif-wrap=no;
base64 — утилита из состава coreurils. Вероятнее всего, что она у вас уже есть;
bash — как известно, жизнь без него в Linux уныла и скучна :)

Как это делается:
ldapsearch ВАШИ_ОПЦИИ -o ldif-wrap=no ФИЛЬТР_ПОИСКА АТРИБУТЫ | \
while read l; do echo $l | grep '^\([^:]\+: \|$\)' || echo "${l%%:: *}: $(base64 -d <<<${l#*:: })"; done

Советую облегчить себе жизнь и не копировать каждый раз код из статьи, а добавить соотв. функцию в свой «домашний» ~/.bashrc. Выполните данный код в командной строке, в конце (после EOF) нажмите ENTER:

cat <<'EOF' >> ~/.bashrc

un64ldif () {
 while read l; do
  echo $l | grep '^\([^:]\+: \|$\)' || \
   echo "${l%%:: *}: $(base64 -d <<<${l#*:: })"
 done
 return 0
}
EOF

Сделав это единожды, вы теперь сможете творить чудеса, передавая вывод ldapsearch своей функции по трубе вводы-вывода:
ldapsearch ВАШИ_ОПЦИИ -o ldif-wrap=no ФИЛЬТР_ПОИСКА АТРИБУТЫ | un64ldif

BASH Tips&Tricks #0003: Запись stdin в файл под sudo

  • BASH
С утилитой sudo есть одна маленькая, но весьма досадная проблема: она, конечно, исполнит указанную команду под каким угодно пользователем и может сделать это даже тихо и незаметно, без ввода пароля, но вот перенаправление ввода-вывода будет отрабатывать уже под обычным пользователем.
Возникла у меня задачка: нужно записать строчку /opt/OpenLDAP/current/Binaries/lib в файл /etc/ld.so.conf.d/OpenLDAP.conf — с целью, понятное дело, вполне тривиальной: заставить систему читать библиотеки OpenLDAP оттуда, откуда нужно мне, а не ей.

Итак, давайте сделаем вид, что мы наивные и попробуем поступить прямолинейно:
sudo echo '/opt/OpenLDAP/current/Binaries/lib' > /etc/ld.so.conf.d/OpenLDAP.conf
Опа! Permission denied, кто бы мог подумать :)

Читать дальше →

BASH Tips&Tricks #0002: Форматируем вывод ldapsearch

  • BASH
Если вам случалось писать на BASH скрипты, работающие с LDAP, то вы наверное уже в курсе, что клиентская утилита ldapsearch, входящая в поставку OpenLDAP, а потому имеющаяся в комплекте большинства дистрибутивов Linux, из каких-то своих соображений (оказывается, это требование RFC 2045) выводит результирующий LDIF со строками, длина которых не превышает 78 символов. Соответственно, те строки, что оказываются длиннее 78-и символов, попросту разбиваются по правилам формата LDIF: в начале продолжения предыдущей строки ставится один пробельный символ.
Анализ такого LDIF на языке оболочки bash может быть затруднительным, гораздо удобнее, когда значение атрибута умещается в одной строке.
Читать дальше →

BASH Tips&Tricks #0001: Распахнём "запахнутые" окна! :)

  • BASH
Надоело восстанавливать сессию screen при каждом заходе по ssh?
Впишите в .bash_profile:

if [[ $PS1 && $(</proc/$PPID/cmdline) =~ ^sshd ]] && { which screen || alias -p screen; } &>/dev/null ; then
  screenSessMaxPID=$(screen -ls | sed -nr 's%^\s+([0-9]+)\..*\((At|De)tached\)$%\1%p' | sort -n | tail -1) && \
   screen -dr $screenSessMaxPID 2>/dev/null
fi

UPD[02/09/2015]
— добавил возможность указания screen'а в виде алиаса
— убрал флуд screen'а при старте

UPD[29/02/2016]
«Интерактивная» версия:

if [[ $PS1 && $(</proc/$PPID/cmdline) =~ ^sshd ]] && { which screen || alias -p screen; } &>/dev/null; then
        declare -a lstScreens=( $(screen -ls | sed -nr 's%^\s+([0-9]+)\..*\((At|De)tached\)$%\1%p' | sort -rn) )
        declare -i nScreens=${#lstScreens[@]}
        if (( nScreens )); then
                if [[ $nScreens>1 && -t 1 ]]; then
                        echo -e 'Warning: there are more than one active screen sessions running simultaneously!\nPlease, choose what session to load' >&2
                        select screenSessID in ${lstScreens[@]}; do [[ $screenSessID ]] && break; done
                else
                        screenSessID=${lstScreens[0]}
                fi
                screen -dr $screenSessID 2>/dev/null
        else
                if [[ -t 1 ]]; then
                        echo 'Info: The are no screens to attach, creating new session' >&2
                        sleep 1
                fi
                screen
        fi
fi

Новая версия скрипта для бэкапа OpenLDAP-баз

  • LDAP
Каждый раз создаётся полный бэкап slapcat'ом
Если предыдущего бэкапа нет (первый запуск, например) — просто делаем первый полный бэкап
Если есть предыдущий полный бэкап, сравниваем текущий полученный бэкап с предыдущим
Если разница есть, то она записывается degrade-патчем (то есть позволяющим получить предыдущую версию из самой свежей), если разницы нет — просто чистятся файлы в /tmp

Если директории для бэкапов нет, она создаётся

Все переменные настроек — в начале скрипта

Читать дальше →

Стратегия бэкапа OpenLDAP

  • LDAP
Для того, чтобы сделать заведомо консистентный (непротиворечивый, целостный то есть) бэкап всех баз OpenLDAP, нужно:
1) Перевести соответствующую базу в режим работы read-only, если до этого она уже не работала в этом режиме;
2) Сделать полнотекстовый дамп базы с помощью утилиты slapcat, входящей в поставку серверной части OpenLDAP (более того, это просто (сим/хард)линк на slapd — бинарник самого сервера);
3) Если режим работы базы изменялся, вернуть его состояние к исходному.

Данный нехитрый алгоритм реализует приведённый ниже скрипт:

Читать дальше →

Скрипт для переключения в режим энергосбережения

  • Linux
Иногда в своём любимом Mandriva Linux мне совсем не хочется пользоваться графическими кнопочками, на которых написано «Ждущий режим», «Спящий режим» и прочие непонятные вещи. И тогда после сеанса успешной борьбы с ленью мне на помощь приходит старый-добрый BASH:
#!/bin/bash
pfx='psm_'
slf="${0##*/$pfx}"
[[ $slf =~ ^($(tr ' ' '|' </sys/power/state))$ ]] || exit 1
if (($(id -u) == 0)); then
 echo -n $slf > /sys/power/state
else
 sudo $0
fi

Сохраните этот скрипт под именем, например, psm_mem в каталоге $HOME/bin (надеюсь, он у вас прописан в PATH?) и создайте на него симлинки:
cd ~/bin
for s in $(sed 's%mem%%' /sys/power/state); do ln -s psm_mem psm_${s}; done

Пропишите своему юзеру в sudoers беспарольное право выполнения соотв. комманд — и вуаля!
По команде psm_mem мы оказываемся в power saving mode, в котором на память подаётся напряжение, а всё остальное тихо отдыхает. И как это называется на графических кнопочках, и где эти кнопочки следует искать — всё это вас уже не колышет беспокоит ни разу!
P.S. Ура :)