Skriptování v Bournově shellu/Modularizace

◄ Skriptování v Bournově shellu/Soubory a proudy Modularizace Skriptování v Bournově shellu/Zpracování signálů a ladění ►

Když budete nějakou chvíli programovat, jistě dříve či později narazíte na některý z následujících problémů:

  • Při psaní programu si uvědomíte, že právě píšete kód provádějící něco, co už jste dříve implementovali na jiném místě.
  • Všimnete si, že program je natolik dlouhý, že se stává nepřehledným.

Tedy zjistíte, že by bylo lepší rozdělit program na podprogramy, na jednotlivé moduly, které je možno volat dle potřeby. Z tohoto hlediska se Bournův shell neliší od jiných programovacích prostředí. Dřív nebo později se pokusíte napsat skript, který bude neprakticky dlouhý. A nejpozději v ten okamžik bude čas začít s modularizací.

Pojmenované funkce editovat

Nejjednodušším postupem z hlediska toho, co jsme se naučili, by bylo rozdělit skript na několik menších skriptů. Takový přístup má ale i nevýhody — spravovat mnoho souborů skriptů, které na sobě komplikovaným způsobem závisejí, je z hlediska náročnosti údržby jen malé zlepšení. Naštěstí nabízí Bournův shell i nástroj umožňující modularizaci v rámci jednoho souboru: pojmenované funkce.

Je to možnost přiřadit k zvolenému jménu sestavu příkazů, které lze pak kdykoliv provést zavoláním daného jména. Z hlediska syntaxe to vypadá následovně:

jméno () skupina příkazů

kde jménem je zvolené jméno právě vznikající funkce a skupina příkazů je jejím obsahem ­ může být vymezena kulatými nebo složenými závorkami.

Tato vymoženost je v Bournově shellu a jeho následovnících přítomna stále, můžete ji použít v shellovým skriptech, ale můžete ji použít i když používáte shell v interaktivním režimu. Může například zastoupit funkcionalitu alias nabízenou některými novějšími shelly.

Vytváření pojmenovaných funkcí editovat

Funkce jako jednoduché skupiny příkazů editovat

Začneme jednoduchým příkladem funkce, jejímž úkolem je vypsat pozdrav „Ahoj lide!“. Pojmenujeme ji „al“ a vytvoříme ji takto:

al () {
echo 'Ahoj lide!';
}

Postup vytvoření funkce je opravdu úplně stejný v interaktivním shellu i shellovém skriptu. Povšimněme si několika maličkostí. Především vidíme, že k vytvoření funkce jsme nepotřebovali žádné klíčové slovo, k rozpoznání, že jde o definici funkce, stačí shellu dvojice kulatých závorek. Ve skutečnosti je z hlediska shellu funkce vlastně jen zvláštním druhem proměnné — jejím obsahem jsou příkazy. Definice funkce je součástí běhové prostředí shellu stejně jako definice proměnných, obé je z hlediska shellu totéž: dvojice jména a jeho obsahu.

Druhou věcí, které si všimneme, že za kulatými závorkami už platí všechna normální pravidla shellu ohledně skupiny příkazů. Použili jsme vymezení funkce složenými závorkami, proto bylo potřeba před uzavírající závorku napsat středník. Řetězec, který jsme chtěli vypsat, obsahuje vykřičníky, proto jsme jej museli obklopit jednoduchými uvozovkami (jako obvykle). A jako obvykle můžeme skupinu příkazů napsat přes několik řádků — i v interaktivním shellu.

Ještě si pro úplnost ukažme zavolání naší funkce, je přímočaré:

$ al
Ahoj lide!

Funkce běžící jako zvláštní proces editovat

Jak už jsme zdůraznili, funkci můžeme definovat pomocí obou druhů skupin příkazů, nejen se složenými závorkami, ale i s kulatými závorkami. O těch si pamatujeme z kapitoly o řízení běhu#Seskupování příkazů, že jimi ohraničená skupina příkazů je spuštěna jako podproces. A u funkcí to funguje úplně stejně. Funkce definovaná s kulatými závorkami je pouštěna jako podproces, s vlastní kopií běhové prostředí, jejíž změny neovlivní prostředí, z něhož je volána. Ukažme si to na příkladu a definujme dvě funkce, které mění hodnotu proměnné POZDRAV:

pozdrav1 () (
POZDRAV='Ahoj Ladislave!'
)

pozdrav2 () {
POZDRAV='Ahoj Bohumile!'
}

A nyní zkusme tyto funkce použít v následujícím skriptu:

$ POZDRAV='Ahoj Břetislave!'
$ echo $POZDRAV
Ahoj Břetislave!
$ pozdrav1
$ echo $POZDRAV
Ahoj Břetislave!
$ pozdrav2
$ echo $POZDRAV
Ahoj Bohumile!

Tedy změna proměnné POZDRAV ve funkci pozdrav1 se v rodičovském prostředí neprojeví.

Funkce s parametry editovat

Pokud jste zkušení programátoři, jistě víte, že nejužitečnější bývají funkce s parametry, funkce jejichž běh není vždy stejný, ale závisí na tom, jaké hodnoty jsou jim předány. Jak je to s parametrizovanými funkcemi v Bournově shellu? Především nutno říci, že volání parametrizované funkce s parametry v kulatých závorkách, jak je standardizováno ve zbytku programátorského světa, je v shellu nemožné. Závorky už mají příliš jiným významů.

Na druhou stranu, pro shell je volání funkce stejné jako volání jiného programu, takže je mu možné předat parametry příkazové řádky. A ty může funkce zpracovat stejným způsobem, kterým shellový skript zpracovává své parametry příkazové řádky, tedy tak, jak jsme se naučili v kapitole o běhovém prostředí. Ukažme si jednoduchý příklad:

ozvena () { echo $1; }

Zavoláním

ozvena 'Ahoj lide!'
ozvena 'Ahoj Ladislave!'

dostaneme dle očekávání výstup

Ahoj lide!
Ahoj Ladislave!

A jak je to v situaci, kdy je funkce volána v rámci skriptu, který má své vlastní parametry příkazové řádky? K těm se v rámci funkce přímo nedostaneme, číslované proměnné mají funkci význam výhradně parametrů dané funkce. Ukážeme si příklad opět s dříve definovanou funkcí ozvena. Napíšeme skript

echo $1
ozvena
ozvena 'Ahoj lide!'
ozvena $1
echo $1

Ten zavoláme s parametrem:

./skript.sh 'Ahoj vesnice!'

A výpisem bude

Ahoj vesnice!

Ahoj lide!
Ahoj vesnice!
Ahoj vesnice!

Jak vidíme, parametr $1 shellového skriptu je sám o sobě ve funkci nečitelný, funkce $1 interpretuje zásadně jako svůj první parametr. Je-li prázdný, nevypíše se nic.

Funkce v prostředí editovat

Už jsme to nakousli, ale nyní se o tom ještě zmiňme podrobněji: Co je to vlastně funkce z pohledu Bournova shellu? Na jednu stranu je to přezdívka pro souhrn příkazů, takové makro. Ale především je to proměnná prostředí. Stejně jako jiné proměnné je to jen dvojice jména (tedy textu) a jeho obsahu (tedy opět textu). Snadno si ověříte, že se jedná jen o proměnnou, napíšete-li v interaktivním shellu příkaz set, který vypisuje definované proměnné. Ve výpisu budou i funkce.

A protože jsou funkce vlastně jen proměnné, vztahují se na ně i různá pravidla o chování proměnných:

  • Stejně jako jiné proměnné se funkce definují trojicí jméno, definiční operátor a hodnota. Rozdíl je jen v tom, že funkce mají jako definiční operátor dvojici kulatých závorek „()“ místo rovnítka „=“. To shell upozorňuje, že je to přeci jen zvláštní druh proměnné. Například pro nahrazení jména obsahem není potřeba používat znak „$“, stačí zmínit jen její jméno.
  • Protože jsou funkce součástí prostředí, jsou přístupné stejně jako jiné proměnné i v prostředí volaného příkazu.
  • Funkce mohou být předány i podprocesům, jsou-li patřičně označeny příkazem 'export'. Některé modernější shelly vyžadují pro export funkcí zvláštní přepínač (například Bash vyžaduje přepínač '-f').
  • K vymazání funkce, stejně jako k vymazání jiné proměnné, se používá příkaz 'unset'.

Protože se jinak funkce chovají jako jiné příkazy, je možné například přesměrovávat jejich vstup a výstup.

◄ Skriptování v Bournově shellu/Soubory a proudy Modularizace Skriptování v Bournově shellu/Zpracování signálů a ladění ►