Tablice asocjacyjne w Bashu

21 maj 2014

Chociaż dla większości użytkowników Bash służy jedynie jako interaktywna powłoka i ewentualnie do pisania prostych skryptów głównie składających się z pętli, z każdym nowym wydaniem dodawane są nowe funkcje, które zbliżają go do pełnoprawnego języka programowania.

Jeżeli nowością można nazwać funkcje dodane w 2010 roku, to jedną z nich jest obsługa tablic asocjacyjnych. Chociaż przy większości skryptów okazują się kompletnie nieprzydatne, znalazłem dla nich zastosowanie.

Abu utworzyć tablicę asocjacyjną, należy ją odpowiednio zadeklarować za pomocą declare, w celu jasnego odróżnienia niej od zwykłej tablicy.
declare -A tablica
tablica=([jeden]=1
         [dwa]=2
         [trzy]=3)

Jak można się było spodziewać, do poszczególnych elementów można odwołać się analogicznie jak w przypadku zwykłej tablicy:

% echo ${tablica[jeden]}
1

Podobnie jest z dodaniem (lub nadpisaniem) elementu o określonym kluczu czy też iteracją przez wszystkie elementy oraz klucze w tablicy:

% tablica[cztery]=4
% echo ${tablica[cztery]}
4

% for element in "${tablica[@]}"; do echo $element; done
3
2
1
4

% for klucz in "${!tablica[@]}"; do echo $klucz${tablica[$klucz]}; done
trzy – 3
dwa – 2
jeden – 1

Należy zawsze pamiętać, że Bash nie gwarantuje w żaden sposób kolejności kluczy przechowywanych w tablicy asocjacyjnej. W tym przypadku elementy określone podczas inicjalizacji tablicy zostały umieszczone w odwrotnej kolejności, a nowy element trafił na koniec listy. Po kolejnej aktualizacji Basha powyższe pętle mogą zwrócić jeszcze inny wynik.

Do czego praktycznego można użyć tablic asocjacyjnych? W moim przypadku była to deduplikacja kodu w PKGBUILDach odpowiedzialnych za dostarczanie kilku wersji językowych. Za wspaniały antyprzykład może posłużyć libreoffice-i18n: 1629 linii z których ponad 1200 to dublujący się kod obsługujący 113 języków dostarczanych przez LibreOffice. Po zastosowaniu tablicy asocjacyjnej, eval oraz curla, PKGBUILD schudł do 354 linii, z których 165 to sumy kontrolne. Zainteresowani mogą podejrzeć efekty tutaj

W podobny sposób zmodyfikowałem pakiet gimp-help, którego pierwsza wersja przypuszczalnie powstała na bazie thunderbird-i18n oraz firefox-i18n. Nieskromnie swoją metodę uważam za lepszą – zabawa w parsowanie białych znaków w przypadku Basha jest niebezpieczna i nie gwarantuje lepszej wydajności od tablic asocjacyjnych. Jedyna potencjalna korzyść to większa zgodność z POSIX – co jest kompletnie bez znaczenia biorąc pod uwagę dokąd prowadzi /bin/sh w Archu i w którym języku napisane jest makepkg.