BASH Tips&Tricks #000F: Ваша перловка, сэр!

BASH
Я часто использую ассоциативные массивы BASH, и все, даже те, кому это было не очень интересно, уже успели познакомиться с этим истинным предметом моего обожания. Иногда я даже генерирую эти ассоциативные массивы, они же «хэши», и складываю их в отдельные include-файлики «на память». Разумеется, это совсем не связано с тем, что реализация «хэшей» в BASH очень медленная и сама по себе их генерация может отнимать столько же процессорного времени, сколько потребовалось бы для получения числа «пи» с точностью до 1000-ного знака после запятой.
Но случается иногда с BASH-программистами казус (не имеющий отношение к глубокоуважаемому Кукоцкому), когда в их наработанное годами упорного труда тёплое скриптовое счастье врывается злобный Perl и требует переписать всё с нуля. Почему? Ну хотя бы потому что гладиолус. Шутка. На самом деле BASH действительно безумно медлителен, так что в какой-то момент и у крутых серверных железок, и у вас может банально не хватить терпения, после чего и возникнет вот эта самая поистине революционная идея: а давайте перепишем всё на Perl!
Идея в принципе хорошая, а ещё краше то, что реализовать её можно «компромиссно», используя уже имеющиеся полезные наработки. Так, например, довольно легко подгружаются в Perl «конфигурационные» файлы, они же обыкновенные include'ы, которые ранее использовались в BASH:
(Реальный пример из моего Perl-кода)

my %areaConf;
open (CONF,"<".AREA_CONF_DIR."/$areaID.inc") || die "Cant open ".AREA_CONF_DIR."/$areaID.inc\n";
while (<CONF>) {
    $areaConf{$1}=$3 if (m/^([^=]+?)\s*=\s*(?'quote'['"]?)(.+?)\k'quote'\s*$/);
}

А вот как просто и элегантно можно преобразовать hash-массив из BASH-формата в Perl-формат для последующей его подгрузки директивой require:

#!/bin/bash
...
declare -p hshFromBASH | \
 sed -r "s&declare -A ([^=]+)='&%\1=&; s%\[%'%g; s%\]%'%g; s%=([^(])%=>\1%g" | \
  sed -r -e s%\"\\s\'%\",\ \'%g -e s%\\\)\'%\)\;\ %g > hsh.pl

Соответственно, здесь сначала директивой «declare -p» выводится «нормализованная» форма декларации ранее сформированного хэш-массива, а затем двумя последовательными поточными правками sed'ом мы получаем ту же декларацию, но уже для Perl. Безусловно, этот код не идеален и где-то будет спотыкаться, но для большинства простых случаев он отлично подходит.
Последнюю версию готовой функции для генерации Perl-хэшей из ассоциативных массивов BASH вы можете скачать из github: github.com/DRVTiny/bash4-debug-infra/blob/master/perl.inc
В качестве домашнего задания предлагаю разобраться с кодом на Perl, который берёт определение «обычного» BASH-массива из файла, преобразует это определение в Perl-формат и загружает готовое описание массива с использованием eval:

#!/usr/bin/perl
sub BASHArr2List {
 my $ArrDef=shift; chomp $ArrDef;
 my ($ArrName)=($ArrDef=~m/^declare -a ([^=]+)=/);
 $ArrDef=~s%(?:^declare -a ${ArrName}='\(|\)'$)%%g;
 $ArrDef=~s%(\[[0-9]+\]="[^"]+")\s*%\$${ArrName}$1;\n%g;
 chomp $ArrDef;
 return ($ArrName,$ArrDef);
}

my ($name,$def)=BASHArr2List(`/bin/bash -c "source point_forecast_hashes_extended.def; declare -p LBLOrder"`);
eval "my \@${name}";
eval "$def";
print $LBLOrder[128]."\n";

За сим до свиданья, наслаждайтесь свежесваренной перловкой и не забывайте старину BASH: он вам ещё не раз упростит админский труд там, где любой другой язык программирования сделал бы его невыносимым!

2 комментария

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.