Skriptování v Bournově shellu/Zpracování signálů a ladění

◄ Skriptování v Bournově shellu/Modularizace Zpracování signálů a ladění Skriptování v Bournově shellu/Příloha A: Přehled příkazů ►

Ladění editovat

V předchozích kapitolách jsme si řekli o skriptování v Bournově shellu skoro vše. Pokryli jsme všechny myslitelné detaily, takže teď umíte skriptovat nejlépe, jak to jde. Ale ať už máte znalosti sebelepší a skripty píšete sebepozorněji, časem se setkáte se situací, kdy skript nebude pracovat, jak má, přestože si budete jisti, že by pracovat měl. Co dělat v takové situaci?

V této kapitole si probereme nástroje, které nabízí Bournův shell pro ladění skriptů. Tedy pro zkoumání jejich nečekaného chování i pro zkoumání toho, jaké signály dostávají zvenčí.

Ladicí příznaky editovat

Tak si to představme: Jsme uprostřed noci a právě jste dokončili dlouhý a složitý skript, do kterého jste tři dny vkládali to nejlepší ze svého umění a tvořivosti jsouce živi jen z kofeinových nápojů a donášky rychlého občerstvení … a on nefunguje. Někde je v něm chyba, ale uniká všemu pátrání. Něco je špatně, nějaké chování se odchýlilo od očekávání, zkrátka máte problém a nedokážete si s ním poradit. Jakým způsobem se nejlépe pustit do ladění skriptu? Jedna možnost by byla prošpikovat ho různými příkazy výpisu a tím podrobněji zkoumat jeho chování … ale nešlo by to nějak pohodlněji?

Obecně vzato je tím nejlepším způsobem, jak ladit nějaký program, sledování jeho provádění krok za krokem. Pozorovatel tak přesně vidí, co program dělá. Nejlépe se to dělá nějakým stopovacím program (často bývá součástí moderních vývojových prostředí), který také umožňuje pozastavit provádění programu na zadaném místě a prozkoumat jeho vnitřní stav. Tohle Bournův shell bohužel sám o sobě nenabízí, ale stopování programu po příkazech ano: totiž vypisování všech prováděných příkazů na obrazovku.

Zapnout stopování je možné dvěma způsoby: buď nastavením příkazem 'set' nebo parametrem příkazové řádky. A jedná se hned o dva způsoby stopování:

-v
Zapíná vykecávací mód, kdy je každý příkaz vypisován v okamžiku, kdy si ho Bournův shell přečte.
-x
Zapíná vykecávací mód, kdy je každý příkaz vypisován v okamžiku, kdy je prováděn.

Ladění editovat

Uvažujme následující skript:

#!/bin/sh
DELITEL=${1:-0}
echo $DELITEL
expr 12 / $DELITEL

Spusťme tento skript bez parametrů (obsahem proměnné DELITEL se tak stane přednastavená nula):

$ sh skript.sh
0
expr: dělení nulou

Samozřejmě není těžké odhadnout, v čem je v tomto případě problém, ale přesto si s tím zkusme ještě pohrát. Vyzkoušíme, co se stane, pokud je shell prováděn s parametrem -x:

$ sh -x skript.sh
DIVISOR=0
echo 0
expr 12 / 0
expr: dělení

Z tohoto výpisu je zřejmé, že opravdu došlo k pokusu dělit nulou. Zkusme ovšem ještě druhý z ladicích příznaků, -v:

$ sh -v skript.sh
#!/bin/sh
DIVISOR=${1:-0}
echo $DIVISOR
0
expr 12 / $DIVISOR
expr: dělení nulou

Tentokrát vidíme přímo i jaká proměnná měla onen nulový obsah, kde se to dělení nulou vzalo. Můžeme ovšem zkusit i oba příznaky zároveň:

$ sh -x -v skript.sh
#!/bin/sh
DIVISOR=${1:-0}
+ DIVISOR=0
echo $DIVISOR
+ echo 0
0
expr 12 / $DIVISOR
+ expr 12 / 0
expr: dělení nulou

A vidíme, že dostáváme maximum informací.

Ještě zmiňme jeden parametr Bournova shellu: -n. Ten způsobí, že shell příkazy pouze čte, ale nevykonává. To se může hodit například pro syntaktickou kontrolu.

Zadávání parametrů editovat

Jak je vlastně možné shellu předat parametry? Buď přímo z příkazové řádky, tedy například spouštíme-li skript takto:

sh -xv skript.sh

další možností je umístit parametry přímo do prvního řádku shellu:

#!/bin/sh -xv

a ten pak volat už jen

./skript.sh

Pokud bychom ho zavolali

sh skript.sh

tak se ovšem ladicí příznaky z prvního řádku skriptu nepoužijí — první řádek se bere v úvahu jen při přímém spouštění bez udání interpretu. Museli bychom zase udat parametry i při volání.

Podobně pokud použijeme operátor tečky

. skript.sh

tak se první řádek nepoužije. V takovém případě je totiž interpret pevně dán - je jím shell, v kterém byl operátor tečky použit. V tomto případě si můžeme pomoci příkazem set:

set -xv
. skript.sh
set +xv

Tedy nejprve zapneme ladicí příznaky, pak pomocí tečky provedeme současným shellem daný skript a pak ladicí příznaky vypneme. Ve výpisu se nám v takovém případě ukáže i samotné načítání skriptu:

 . divider.sh
 + . divider.sh
 #!/bin/sh -vx
 
 DIVISOR=${1:-0}
 ++ DIVISOR=0
 echo $DIVISOR
 ++ echo 0
 0
 expr 12 / $DIVISOR
 ++ expr 12 / 0
 expr: division by zero

Ještě si můžeme vyzkoušet, zda by se nastavení příznaku ve volajícím shellu projevilo laděním ve volaném i při obvyklém spuštění:

set -xv
sh skript.sh
set +xv

Odpověď je jednoduchá: Neprojeví. Nastavení se týká shellu, ve kterém jsme, a do volaného se nepřenáší. Jako poučení z těch pokusů si odnesme poznání, že je při ladění důležité vědět, v kterém shellu vlastně ladicí příznaky zapínáme.

Přerušení skriptu editovat

Při ladění (a vlastně i při rutinních bězích) může být někdy užitečné v nějakém okamžiku skript předčasně ukončit. K tomu můžeme použít vestavěný příkaz 'exit'. Jeho syntaxe je prostá:

exit [n]

kde nepovinný parametr n udává návratovou hodnotu. Není-li udán, použije se jako návratová hodnota návratová hodnota příkazu předcházejího příkaz 'exit'.

Při používání příkazu 'exit' je třeba dát pozor na to, že ukončuje interpretující shell. To znamená, že „spustíme-li“ skript pomocí tečky, pak v něm obsažený příkaz '€xit' nezpůsobí ukončení jen „podskriptu“ pouštěného tečkou, ale i skriptu „volajícího“. Na stejný problém narazíme i při pokusu ukončit funkci neběžící jako zvláštní proces.

Právě proto je k disposici ještě jeden vestavěný příkaz, return. Ten ukončuje skupinu příkazů, která není samostatným procesem. Jinak se neliší, i on má nepovinný parametr s návratou hodnotou. Ukažme si ho na příkladu:

#!/bin/sh

vypisAhoj() {
  echo 'Ahoj!!'
  return 2
}

echo 'Ahoj, lidi!!'
vypisAhoj
echo $?
echo 'Nashledanou!!'
exit

Pokud tento skript pustíme, vypíše následující:

./exit_and_return.sh
Ahoj, lidi!! 
Ahoj!!
2
Nashledanou!!

Funkce skončila s návratovou hodnotou 2, kterou můžeme z vnějšku otestovat. Návratovou hodnotou celého skriptu je ovšem nula.

Teoreticky je možné použít return i na konci celého skriptu. Pokud jej pustíme tečkou, aby byl prováděn aktuálním shellem, bude to fungovat bez potíží, ovšem pokud zkusíme takový skript pustit se samostatným shellem, skončí volání chybou. Takové použití tedy skript předurčuje k tomu, aby byl interpretován vždy pouze již běžícím shellem, což je obvykle nevýhodné omezení.

◄ Skriptování v Bournově shellu/Modularizace Zpracování signálů a ladění Skriptování v Bournově shellu/Příloha A: Přehled příkazů ►