Iterowanie po tablicach w Bashu || tablice część pierwsza

Przy okazji tego wątku na Forum, postanowiłem odświeżyć sobie działanie tablic w Bashu.

Poprosiłem użytkownika, żeby sprawdził, jakie pakiety ma zainstaloane, a jakich nie ma w ten sposób:

#!/bin/bash

RPMS=(  TESTOWY_NIE_MA_TAKIEGO_PAKIETU nie_ma cheese-lang gdm-lang gnome-contacts-lang
        gnome-control-center-color
        gnome-control-center-goa gnome-control-center-lang gnome-session-lang gnome-session-wayland
        gnome-shell-calendar gnome-shell-classic gnome-shell-extension-gpaste gnome-shell-lang
        gnome-shell-search-provider-bijiben gnome-shell-search-provider-contacts
        gnome-shell-search-provider-documents gnome-shell-search-provider-nautilus gnome-tweaks-lang
        gstreamer-plugins-bad-lang libpurple-lang libpurple-tcl pidgin pulseaudio-gdm-hooks cheese
        gnome-shell-extension-desktop-icons gnome-contacts gnome-tweaks libpurple patterns-gnome-gnome
        patterns-gnome-gnome_imaging patterns-gnome-gnome_office patterns-gnome-gnome_utilities
        libpurple-branding-openSUSE patterns-gnome-gnome_x11 gstreamer-plugins-farstream
        patterns-gnome-gnome_basic libfarstream-0_2-5 patterns-gnome-gnome_basis gdm-branding-openSUSE
        gdm gnome-shell gnome-session-default-session gnome-session gnome-control-center libcheese-gtk25
        libcheese8 gstreamer-plugins-bad
)

for i in ${RPMS@]}; do
  rpm -q --queryformat "%{NAME}
" $i
done

Działa, ale wynik nie jest posortowany. Dodajmy więc dwie puste tablice:

INSRPMS=()
MISRPMS=()

i poprośmy Basha, by uzupełniał je w zależności od exit code’u: jak jest 0, to paczka jest zainstalowana, a jak nie zero, to paczki nie ma:

for i in ${RPMS@]}; do
  rpm -q --queryformat "%{NAME}
" $i &> /dev/null
  if  $? -eq 0 ]; then
    INSRPMS=(${INSRPMS@]} "$i")
  else
    MISRPMS=(${MISRPMS@]} "$i")
  fi
done

Jak tablica się wygeneruje, to można ją przeiterować, by odczytać wszystkie elementy:

echo "Not installed RPMS:"
for i in ${MISRPMS@]}; do
  echo "  $i is not installed"
done

To też działa, ale czy da się lepiej? No da się! Dodajmy więc funkcję, której przekażemy tablicę jako dane wejściowe:

function _printArray(){
  ARRAY=("$@")
  for i in "${ARRAY@]}"; do
    echo -e "  ${i}"
  done
}

Sprawdźmy, co obie tablice zebrały:

echo "# Installed RPMs:"
_printArray ${INSRPMS@]}
echo -e "
# Missing RPMs:"
_printArray ${MISRPMS@]}

Działa dobrze, ale jakoś tak smutno. Dodajmy trochę kolorów - poprawi to również czytelność wyniku.

W osobnej sesji sprawdźmy, co się stanie, jak to przeklepiemy:

RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'

echo -e "${RED}RED${NC}"
echo -e "${GREEN}GREEN${NC}"

Stosunkowo łatwo można pokolorować nasz wynik, tworząc nowe tablice z zainstalowanymi i brakującymi paczkami:

for i in ${RPMS[@]}; do
  rpm -q --queryformat "%{NAME}
" ${i} &> /dev/null
  if  $? -eq 0 ]; then
    INSRPMS=(${INSRPMS@]} "${GREEN}${i}${NC}")
  else
    MISRPMS=(${MISRPMS@]} "${RED}${i}${NC}")
  fi
done

Ma to zasadniczą wadę. Wynik na ekranie będzie, owszem, ładny, jednak tracimy możliwość dalszej obróbki nowo utworzonych tablic, bo każdy element ma taką formę:

\033[0;32mgnome-shell-lang\033[0m

Spróbujmy więc jako pierwszy element tablicy, którą przekazujemy do naszej funkcji, dodać kolor:

_printArray green ${INSRPMS[@]}

W takiej postaci pierwszym pakietem, który będzie wyszukiwany będzie nazwa koloru, dlatego poprośmy for o to, by liczył elementy w tablicy od drugiego elementu:

${ARRAY@]:1}

Wróćmy do naszego skryptu. Obslużmy przekazany pierwszy element tablicy, czyli kolor, i połączmy wszystko:

#!/bin/bash

RPMS=(  TESTOWY_NIE_MA_TAKIEGO_PAKIETU nie_ma cheese-lang gdm-lang gnome-contacts-lang
        gnome-control-center-color
        gnome-control-center-goa gnome-control-center-lang gnome-session-lang gnome-session-wayland
        gnome-shell-calendar gnome-shell-classic gnome-shell-extension-gpaste gnome-shell-lang
        gnome-shell-search-provider-bijiben gnome-shell-search-provider-contacts
        gnome-shell-search-provider-documents gnome-shell-search-provider-nautilus gnome-tweaks-lang
        gstreamer-plugins-bad-lang libpurple-lang libpurple-tcl pidgin pulseaudio-gdm-hooks cheese
        gnome-shell-extension-desktop-icons gnome-contacts gnome-tweaks libpurple patterns-gnome-gnome
        patterns-gnome-gnome_imaging patterns-gnome-gnome_office patterns-gnome-gnome_utilities
        libpurple-branding-openSUSE patterns-gnome-gnome_x11 gstreamer-plugins-farstream
        patterns-gnome-gnome_basic libfarstream-0_2-5 patterns-gnome-gnome_basis gdm-branding-openSUSE
        gdm gnome-shell gnome-session-default-session gnome-session gnome-control-center libcheese-gtk25
        libcheese8 gstreamer-plugins-bad
)

INSRPMS=()
MISRPMS=()

GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

function _printArray(){
  ARRAY=("$@")
  if [ $1 = "red" ]; then
    COL=$RED
  elif  $1 = "green" ]; then
    COL=$GREEN
  fi

  for i in "${ARRAY@]:1}"; do
    echo -e "  ${COL}${i}${NC}"
  done
}


for i in ${RPMS@]}; do
  rpm -q --queryformat "%{NAME}
" ${i} &> /dev/null
  if  $? -eq 0 ]; then
    INSRPMS=(${INSRPMS@]} "${i}")
  else
    MISRPMS=(${MISRPMS@]} "${i}")
  fi
done

echo "Installed RPMs:"
_printArray green ${INSRPMS@]}
echo -e "
Missing RPMs:"
_printArray red ${MISRPMS@]}

Przydatne linki: