Skript pro porovnání verzí 2 (komentovaný kód)

V minulém článku jsem probral teoreticky, jak funguje skript pro odstranění starých balíčků z mého repozitáře. Na konci článku jsem slíbil komentovaný kód s tím, že u každého řádku napíšu proč tam je a co dělá. No… a ten článek čtete. 

Začátek skriptu a vstup

  1. #!/bin/bash

Na začátku každého skriptu je nutné (nanejvýše vhodné) uvést program, ve kterém se daný skript bude spouštět.

Pro ty, které to zajímá, to funguje tak, že pokud je pokud je první neprázdný znak křížek a za ním hned vykřičník, shell považuje řádek za těmito znaky za cestu k programu, který se má spustit. Jako parametr je danému programu poté předložena cesta ke skriptu. Tímto mechanizmem se zaručí, že pokud napíšete BASHový skript plný bashizmů (tak jak to děláme všichni 😉 ), tak bude spuštěn v BASHi, nikoli v csh nebo zsh. Stejně tak je problém s pythonem/ruby… dle tohoto řádku budou python skripty vždy spuštěny v pythonu, ruby skripty v ruby atd. Samozřejmě, je nutné mít v pythonu napsáno python atd.

Krátká funkce, která po zavolání vypíše nápovědu a ukončí program. Nápověda je především pro mě a důležité jsou jediné tři řádky na konci.

  1. function print_help () {
  2. echo "Program pro porovnani dvou verzi
  3. Pouziti: $0 [-q] VER1 VER2
  4. -h -- vypise napovedu
  5. --help -- vypise napovedu
  6. -q -- tichy rezim,
  7. nebude vypisovat nic na terminal
  8.  
  9. Navratove hodnoty:
  10. \$? = 0 VER1 == VER2
  11. \$? = 1 VER1 > VER2
  12. \$? = 255 VER1 < VER2
  13. \$? = 99 ERR"
  14. exit 0
  15. }

Kontrola parametrů a případné vypsání nápovědy při parametru -h a --help. Chybová hláška v případě nevhodných parametrů.

  1. if [ "$1" = "-h" ]; then
  2. print_help
  3. elif [ "$1" = "--help" ]; then
  4. print_help
  5. elif [ "$1" = "-q" ]; then
  6. QUIET=1
  7. shift
  8. fi
  9. if [ $# != 2 ]; then
  10. echo "Musi byt postoupeny dva parametry, nebo tri s parametrem -q. Pro help napiste: $0 -h"
  11. exit 99
  12. fi

Všimněte si především řádků 22 a 23. Pokud je první proměnnou řetězec "-q", tak se na řádku 22 se do lokální proměnné QUIET uloží číslo 1 (vytvoří nenulovou délku této proměnné). Ta se poté bude testovat — pokud bude existovat (délka bude nenulová) bude zapnut tichý režim a nebudou se vypisovat žádné hlášky na terminál. Příkaz shift následně vezme všechny proměnné, první zahodí a zbytek proměnných posune o místo dopředu.
Tímto trikem jsem docílil toho, že ve zbytku skriptu bude na místě první proměnné ($1) vždy právě první parametr pro porovnání a na místě druhé proměnné ($2) druhý parametr.

Probírání proměnných a nastavení proměnných

Následující dva řádky zajisté vyvolají hrůzu všech, co nejsou plně znalí nástroje sed. A nejspíše i těch, co znalí jsou.

  1. LEFT=$(echo $1 | sed s/svn/0.1./g | sed s/alpha/0.2./g | sed s/beta/0.3./g | sed s/[rR][cC]/0.4./g | tr '.' ' ' )
  2. RIGHT=$(echo $2 | sed s/svn/0.1./g | sed s/alpha/0.2./g | sed s/beta/0.3./g | sed s/[rR][cC]/0.4./g | tr '.' ' ' )

Pamatujete, jak jsem v minulém článku psal o převádění svn na 0.1.? alpha na 0.2.? beta na 0.3.? rc na 0.4.? A o změně teček za mezery? Jo, tak to je ono.

Jak to funguje? Do proměnné LEFT/RIGHT ukládám upravenou verzi prvního/druhého parametru. Konkrétní forma úpravy je následující:

  1. Obsah prvního parametru pošleme do programu sed, který přemění všechny svn na 0.1.
  2. výsledek pošlu opět do sedu, kde vyměním string alpha za 0.2.
  3. výsledek pošlu opět do sedu, kde vyměním string beta za 0.3.
  4. výsledek pošlu opět do sedu, kde vyměním string RC za 0.4. (bez ohledu na velikost písmen)
  5. výsledek pošlu opět do sedu, kde vyměním dvě tečky vedle sebe za jednu tečku
  6. a nakonec vyměnění tečky za mezeru

Proč takto složitě? Protože v jednom řádku udělám všechny změny.

Ověřím, že v proměnných nezůstal žádný znak navíc:

  1. if [ -n "$(echo $LEFT | grep [a-zA-Z])" -o -n "$(echo $RIGHT | grep [a-zA-Z])" ]; then
  2. if [ ! $QUIET ]; then echo "Nepripustny znak"; fi
  3. exit 99
  4. fi

Pokud zůstane nějaký znak, skript to ukončí. Musí tam být pouze čísla.

Nyní mám již vše připraveno k tomu, abych mohl začít pracovat. Prvně uložíme jednotlivé části verze do pole jménem LEFT_ARR a RIGHT_ARR.

  1. LEFT_ARR=($LEFT) #vytvor pole LEFT
  2. RIGHT_ARR=($RIGHT) #vytvor pole RIGHT

A určíme velikost obou polí, kterou uložíme do LEFTLEN a RIGHTLEN.

  1. LEFTLEN=${#LEFT_ARR[@]} #urci velikost pole LEFT
  2. RIGHTLEN=${#RIGHT_ARR[@]} #urci velikost pole RIGHT

Určíme, které pole je větší a tuto hodnotu uložíme do ARR_SIZE.

  1. if [ $LEFTLEN -ge $RIGHTLEN ]; then
  2. ARR_SIZE=$LEFTLEN
  3. else
  4. ARR_SIZE=$RIGHTLEN
  5. fi

Protože prvky v poli jsou číslovány od nuly, zmenšíme hodnotu ARR_SIZE o jedna

  1. let ARR_SIZE--

Samotný rozhodovací algoritmus

V předelých dvou částech se připravila data tak, aby mohla být zpracována. A nyní přichází vrcholných dvanáct řádků kódu. Připomenu, jak můžou vypadat dvě pole LEFT_ARR a RIGHT_ARR:

Verze \ index      |  1 |  2 |  3 | 4  | indexy
LEFT  = 1.2.66.5   | 1  | 2  | 66 | 5  | LEFT_ARR
RIGHT = 1.3        | 1  | 3  |    |    | RIGHT_ARR

První řádek je pouhá definice ARR_ITEM generované pomocí seq:

  1. for ARR_ITEM in $(seq 0 $ARR_SIZE); do

Postupně do proměnné ARR_ITEM budou ukládána čísla od nuly do hodnoty proměnné ARR_SIZE (to je velikost vetšího ze dvou polí mínus jedna). Tyto čísla budou sloužit jako indexy položek pole.

Zde odkáži na druhý velmi užitečný a málo známý prográmek jménem seq. seq vypíše čísla od prvního parametru do druhého parametru. seq velmi často používám právě v případech, kdy chci něco očíslovat či udělat přesně několikrát.

V případě ( if ), že ARR_ITEM-tý prvek z pole LEFT_ARR je prázdný (to kontroluje část [ ! ${LEFT_ARR[$ARR_ITEM]} ], potom ( then ) jej nastav na nulu ( LEFT_ARR[$ARR_ITEM]=0 ).

  1. if [ ! ${LEFT_ARR[$ARR_ITEM]} ]; then LEFT_ARR[$ARR_ITEM]=0; fi
  2. if [ ! ${RIGHT_ARR[$ARR_ITEM]} ]; then RIGHT_ARR[$ARR_ITEM]=0; fi

A nyní přijde zásadní rozhodování. V případě, že je ARR_ITEM-té prvky v obou polích shodné, budeme pokračovat testováním dalšího (ARR_ITEM+1) — to dělá příkaz continue. V případě, že se liší, se určí, který prvek je větší a skript se ukončí.

  1. if [ ${LEFT_ARR[$ARR_ITEM]} = ${RIGHT_ARR[$ARR_ITEM]} ]; then
  2. continue
  3. elif [ ${LEFT_ARR[$ARR_ITEM]} -gt ${RIGHT_ARR[$ARR_ITEM]} ]; then
  4. if [ ! $QUIET ]; then echo "$1 > $2" ; fi
  5. exit 1
  6. elif [ ${LEFT_ARR[$ARR_ITEM]} -lt ${RIGHT_ARR[$ARR_ITEM]} ]; then
  7. if [ ! $QUIET ]; then echo "$1 < $2" ; fi
  8. exit 255
  9. fi

Dva skoro shodné řádky s

if [ ! $QUIET ]; then echo "$1 > $2" ; fi

a

if [ ! $QUIET ]; then echo "$1 < $2" ; fi

určují, jestli se bude něco vypisovat na terminál (a případně to vypíší) nebo nebude (a skript nenapíše ani řádek).

Ukončíme smyčku započatou na řádku 46.

  1. done

Říkáte si, co když jsou obě čísla úplně shodná? Poté přeci smyčka projde všechny členy pole? Příkaz continue vždy posune počítadlo indexu pole o jeden výše. Pokud jsou všechny verze shodné, poté je načase ukončit skript s návratovou hodnotou nula tak, jak bylo definováno chování skriptu.

  1. if [ ! $QUIET ]; then echo "$1 = $2" ;fi
  2. exit 0

Opět je zde navíc řádek pro otestování tichého modu a případný výpis zprávy na terminál. Příkaz exit 0 ukončuje skript s návratovou hodnotou 0 stejně, jako v předešlých případech byly exit 1 a exit 255.

Shrnutí

Pokud bychom nepotřebovali nápovědu, testování vstupních hodnot a nebo ukecaný režim, skript by měl asi 20 řádků. Já ovšem tvořím skripty tak, aby byly pokud možno blbovzdorné a s nápovědou — aby je mohl použít každý.

3 komentáře u „Skript pro porovnání verzí 2 (komentovaný kód)“

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *