Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Работа в консоли Linux

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