Библиотека сайта rus-linux.net
BashDiff: новые приемы в оболочке Bash
Оригинал: Teach an old shell new tricks with BashDiff
Автор: Ben Martin
Дата: 27 октября 2008
Перевод: Александр Тарасов aka oioki
Дата перевода: 4 ноября 2008
BashDiff - это патч для оболочки Bash, который позволяет совершать новые удивительные вещи. Он расширяет существующие функции Bash, добавляет приемы языка awk в саму оболочку, а также некоторые функции C; добавляет механизм исключений, а также новые возможности функционального программирования, такие как генераторы списков и map-функции; позволяет работать с GTK+2 и базами данных, даже добавляет веб-сервер в стандартную bash-оболочку.
В репозиториях openSUSE, Fedora и Ubuntu нет пакетов BashDiff. Я собирал программу из исходников версии 1.45 на машине архитектуры x86 с установленной Fedora 9, версия Bash 3.0. Уже есть версии Bash 3.1 и 3.2, однако патч BashDiff 1.45 не может быть применен к ним.
Можно собрать оболочку BashDiff с поддержкой разделяемых объектов (которые загружаются при запуске оболочки), либо статически вкомпилировать их в оболочку. Если на вашей машине много памяти, тогда можно попробовать полную сборку BashDiff, в которой уже есть все расширения, и вам не придется их подгружать перед использованием. Я скомпилирую два бинарных файла, bash и bash+william, таким образом покажу два подхода.
Наверняка вы хотите оставить оригинальный файл bash, который был изначально в вашем дистрибутиве. Для этого надо указать отдельный префикс при конфигурировании исходников BashDiff. Например, в /usr/local/bin можно создать ссылки на бинарные файлы, а все файлы BashDiff держать в одном каталоге.
Ниже показаны команды для применения патча BashDiff и последующей компиляции Bash. Во-первых, нужно распаковать tar-архив стандартного Bash 3.0 и извлечь патч из сжатого tar-архива BashDiff. После применения патча, нужно запустить обычные конфигурирование и сборку. Обратите внимание, какой префикс я указал для установки BashDiff. Для установки одних лишь бинарных файлов версии bash+william я указал make-цель install-bin. Последние четыре команды устанавливают разделяемый объект william.so для версии non-william, в которой этот объект загружается динамически.
$ mkdir bashdiff-build $ cd bashdiff-build $ tar xzf ../bash-3.0.tar.gz $ cd ./bash-* $ tar xzvf .../bashdiff-1.45.tar.gz $ patch -p2 < bashdiff-1.45.diff $ autoconf $ ./configure --prefix=/usr/local/bashdiff $ make $ make bash+william $ sudo make install $ sudo make install-bin $ cd examples/loadables/william $ make $ sudo make install $ sudo ldconfig
И последнее замечание насчет компиляции BashDiff: если хотите поддержки SQLite (2.x), MySQL, PostgreSQL, GTK+2, GDBM и Expat, понадобится установить пакеты разработки для этих программ.
BashDiff расширяет команды for, while и until, а именно добавляет блоки then и else, после блока самого цикла. Эти новые блоки then/else позволяют выполнять какие-то действия после обычной итерации блока (блок then) и блок команд, после выполнения команды break (блок else). Блок else полезен в том случае, если хотите выполнить что-либо после досрочного выполнения цикла по команде break.
В Bash 3 есть выражение для генерации последовательности целых чисел - к примеру {1..100}
создает последовательность чисел от 1 до 100, разделенных пробелами. В стандартном Bash нельзя устанавливать в качестве минимального и максимального значений переменные, приходится использовать команду seq. BashDiff позволяет использовать переменные, как в следующем примере:
$ max=15 $ echo {1..max} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
В расширенной команде case разрешено использовать регулярные выражения. Можно получить доступ к вложенным совпадениям через массив SUBMATCH. В BashDiff также реализована функция из Ksh и Zsh - возможность прохода по нескольким веткам case, это делается завершением выражения case с помощью символов ;&
вместо обычных ;;
.
BashDiff добавляет семантику для обработки исключений - команды try и raise. Блок try заканчивается словом done, в него можно включать блок case - для разделения и обработки ошибок. Если ошибка не была обработана в блоке done, тогда она распространяется вверх на блок try более высокого уровня, как и предполагается в исключениях C++ и Java.
Программистов на языке C порадуют функции работы со строками, взятые из языка C. Если вы привыкли к функциям strcpy, strcat, strcmp и strlen, тогда вам проще будет работать с ними в Bash. Еще представлены функции strstr, str[c]spn и strtok, однако их имена слегка изменились. Так, функция strspn в BashDiff называется accept.
Продолжая тему языка C, можно отметить, что в BashDiff включена функция sscanf, позволяющая программисту считывать и разбивать данные на части. В Bash нет различия между строковой и численной переменной, поэтому можно извлекать лишь строки (или отдельные символы); однако в BashDiff есть функция sscanf, при использовании которой можно указывать специальные операторы процента для выделения символов, попадающих в определенный класс. Таким образом можно с легкостью разбивать входную строку на части - либо части будут разделяться пробелом (оператор %s), либо у вас явно численные, буквенные или численно-буквенные данные, каким либо образом обособленные. Ниже показан простой URL-парсер, написанный с использованием команды sscanf:
$ url=http://www.linux.com/foo $ sscanf $url '%[a-zA-Z]://%[^\/]/%[a-zA-Z1-9]' a b c $ declare -p a b c declare -- a="http" declare -- b="www.linux.com" declare -- c="foo"
Если для разбора вашей входной строки обычных возможностей sscanf не хватает, тогда можно разбивать ее на части с помощью регулярного выражения, и помещать полученные подвыражения в массив Bash. В примере ниже я дописал еще один каталог в URL-адрес, и теперь делаю разбор с помощью регулярного выражения. Сначала нужно проверить URL на наличие начальной подстроки http или ftp, затем выбрать доменное имя и первый каталог. Результат разбора match будем помещать в массив. Первый, второй и последний элементы массива имеют специальное назначение и содержат соответственно префикс строки, который не совпадает с регулярным выражением; остальная часть строки, совпадающая с регулярным выражением; и, наконец, завершающая часть строки, также не совпадающая с регулярным выражением - таким образом, можно видеть, что именно было пропущено, что совпало, и что осталось после разбора строки. В остальных ячейках массива хранится, как и следовало ожидать, подстроки исходной строки:
BashDiff предоставляет элементы функционального программирования, а именно команду arraymap, принимающая в качестве аргументов одну команду и несколько массивов (один и более). После этого команда будет выполнена с каждым элементов массива. Когда в функцию передается два и более массивов, тогда элементы этих массивов берутся по порядку. К примеру, ниже показана функция adder, которой при первом вызове передается первые элементы массивов a и b, вторые при втором, и так далее.
$ a=(1 2 3) b=(4 5 6) $ adder() { echo $(($1 + $2)); } $ arraymap adder a b 5 7 9
Еще из функционального программирования пришли генераторы списков вида ${var|command}
. В этом случае команда command выполняется над переменной var, а результат подставляется в итоговое выражение. Для выполнения некоторых полезных предопределенных команд можно пользоваться особыми префиксами. К примеру, следующая команда использует префикс -
для разделения строки регулярным выражением и возвращает несовпадающие блоки в качестве элементов массива. Во входной строке много пробелов после слова "and" и одиночная табуляция после слова "there". В документации BashDiff есть подробное объяснение функций для разделения регулярными выражениями и glob, смены регистра букв и обработки цитат и пробелов.
$ in="and there was singing" $ for z in ${in|-[ \\t]+}; do echo $z; done and there was singing
Операторы минус, плюс и плюс-минус позволяют выбирать переданную переменную с помощью регулярных выражений. В версии минус в качестве разделителя используется регулярное выражение, при этом в качестве результата возвращается то, что осталось между совпадениями. В версии плюс вы указываете, что хотите вернуть в качестве результата. Версия плюс-минус использует символ ± и возвращает как совпадающие, так и несовпадающие строки.
$ echo $url http://www.linux.com/foo/bar $ echo ${url|-/} http: www.linux.com foo bar