5Kapitola

Bash 5: Pole a systémové proměnné

Pole, lány, traktory, králíci... od Bohdana Milara

Vítám vás u dalšího dílu našeho bashového receptáře. Ani jsme se nenadáli a máme tu jaro. Jako správní pěstitelé otevřeného kódu proto vyrazíme na pole. Potom se z širých lánů půjdeme schovat do bezpečí závorek, odkud budeme sledovat proměny (resp. proměnné) naší úrody, abychom nakonec mohli sklízet úrodu ostře nabroušeným promptem.

Jaké pole?

Nebojte se, nespletl jsem se, tento článek nebyl určen do zemědělského časopisu. Polem se zde zabývat budeme, nikoli však ve smyslu polnohospodářském, nýbrž systémovém. A nebude to ani diskové pole. Půjde o pole proměnných (anglicky array či v množném arrays).

O běžných proměnných jsme se již bavili dříve. Je to určité označení (jako např. ono známé x v matematických rovnicích), za kterým se skrývá určitá hodnota, jež se může v průběhu práce systému měnit, a tím ovlivní jeho chování.

Můžeme mít proměnnou kralici, jejíž hodnota bude představovat počet králíků v naší králíkárně. Vedle toho může mít proměnnou husy, kruty, slepice atd. Jejich hodnoty nastavíme rovnítkem (např. kralici="5"). Číst hodnotu pak lze třeba příkazem echo $kralici.

A takhle můžeme definovat zvířectvo, dokud nám bude paměť (a swap) stačit. Po nějaké době ale dospěje většina lidí do situace, kdy potřebuje přistupovat k jednotlivým proměnným na základě nějakého klíče nebo kdy jednotlivé proměnné potřebuje ještě dále členit.

Tak vezměme třeba kralici (jména proměnných neskloňujeme) a chtějme si zvlášť evidovat počet samců a samic. A právě nyní je čas vyrazit na to pole. Na poli je totiž víc místa, abychom si králíci (tedy vlastně králíky) a králice (nebo jak se to správně říká) mohli postavit pěkně do řad.

Řady zvířátek můžeme číslovat a, jak už to tak v Linuxu a podobných systémech bývá, číslujeme od nuly. 0 bude třeba angorský, 1 burgundský a 2 kastorex. Tomuto označení položky pole říkáme index (anglicky subscript). Číslo položky zapisujeme do hranatých závorek bezprostředně za jméno pole.

Práce s polem

Hodnotu určité položce pole přiřadíme s použitím hranatých závorek takto: kralici[0]="3". Pozor ale při práci s hodnotou - tam musíme použít ještě složené závorky, takže echo ${kralici[0]}. Podobně můžeme přiřadit třeba kralici[1]="5" a kralici[2]="4".

Co jsme tím získali? Na první pohled jen tři proměnné se složitěji tvořeným jménem a nutností dávat si pozor na složené závorky. Možná ale změníte názor, když zadáte echo ${kralici[*]}. Jedním příkazem tak můžete získat přehled o hodnotách všech položek pole. Tím ovšem výhody pole nekončí.

Podobně jako můžeme naráz číst, můžeme naráz zapisovat. Vytvořme pole drubez (0 bude třeba kur, 1 husy, 2 kachny) a zkusme jej naplnit hodnotami. Stačí zapsat drubez=( 14 8 6 ). A můžete zkusit echo ${drubez[1]} nebo echo ${drubez[0]} (pro výpis nulté položky lze použít též echo ${drubez}).

Pokud se pokusíme nadefinovat stejné pole znovu, zmizí jeho původní hodnoty. Dobře si to ukážeme na příkladu drubez=( 16 9 ). Nyní jsme definovali nové hodnoty $drubez[0] a $drubez[1]. Pokud zkusíme provést echo ${drubez[2]}, uvidíme, že nic neuvidíme, protože výsledkem bude prázdný řetězec. Novou definicí se totiž celé pole vyčistilo. Nešlo jen o přepsání hodnot dotčených položek pole.

Můžeme definovat hromadně jen některé položky pole jako třeba drubez=( [0]=16 [2]=9 ). Můžeme hromadně doplnit další položky k již existujícím. Jak to udělat, když jsme řekli, že nová definice smaže původní obsah? Obdobně, jako při natahování řetězce v běžné proměnné: drubez=( ${drubez[*]} [3]=6 [4]=4 ).

V případě, že nadefinujeme moc velké pole, které už nepotřebujeme, nebo se chceme pole zbavit z jiného důvodu, máme tady unset. Syntaxe je jednoduchá: unset drubez (nepoužijeme znak dolar, protože pracujeme s proměnnou jako takovou, nikoli s její hodnotou). Stejný efekt by mělo unset drubez[*]. Lze mazat i jednotlivé položky, a to pomocí třeba unset drubez[1].

Velmi důležitou, možná nejdůležitější, vlastností pole je možnost adresovat jednotlivé položky pole pomocí jiné proměnné. Toto tvrzení může znít složitě, ale ve skutečnosti jde o jednoduchou věc. Zkuste si: polozka="1" ; echo ${drubez[$polozka]}. To je prostě věc, kterou s normálními proměnnými neuděláte.

Závorky složené a kulaté

Složené závorky se používají nejen pro oddělení jména proměnné nebo pole od dalších znaků v rámci příkazu. Jak jsme si ukázali ve třetím díle, dokážou také seskupovat několik příkazů do jednoho celku. To se může hodit hned v několika případech - když chceme použít konstrukci, která umí vykonat pouze jeden element, chceme-li najednou přesměrovat výstup z několika příkazů nebo třeba pokud chceme nechat celou skupinu úloh provést na pozadí.

Lze tak bez obav zadat { sleep 10 ; echo Budíček! ; } &. Středník za posledním příkazem je zde nutný, podobně jako mezera za otevírací a před zavírací závorkou. Můžeme použít také závorky kulaté, které mezery ani závěrečný středník nepotřebují. Příkaz by pak mohl vypadat takto: (sleep 10 ; echo Budíček!) &.

Vedle způsobu zápisu se konstrukce s kulatými a složenými závorkami liší ještě v jednom - předávání hodnot proměnných prostředí. To, co uzavřeme do složených závorek, poběží stále v aktuálním Bashi, takže po skončení těchto operací budou provedené změny stále platit. Zadejme husy="5", potom { husy="3" ; echo $husy ; } a nakonec echo $husy.

Na první echo (to v závorkách) by měla být reakce 3, protože jsme v závorkách hodnotu proměnné změnili z 5 na 3. Na druhé echo (za závorkami) bude odpověď zase 3, protože příkazy provedené v závorkách jsou platné pro aktuální shell.

Jinak tomu samozřejmě bude v případě závorek kulatých. Zadejme husy="5", potom (husy="3" ; echo $husy) a nakonec echo $husy. V okamžiku otevření závorky se spustí nový shell, který provede pouze příkazy v závorce. První echo tedy opět vypíše hodnotu 3, kterou nastavujeme uvnitř závorek. Potom se závorka zavře, ukončí se příslušný Bash a s ním zmizí i hodnoty proměnných v něm nastavené. Proto druhý shell vypíše hodnotu 5, kterou jsme v něm nastavili před tím, než byl zavolán druhý Bash.

Již minule jsme se seznámili s proměnnou PATH. Takových proměnných je ale více. Jmenujme jenom některé:

Systémové proměnné

Hodnoty proměnných, které uvádím kolem článku, získáte příkazem echo. Pro výpis všech aktuálních proměnných a jejich hodnot slouží příkaz env, který můžeme spustit bez parametrů.

Za zvláštní zmínku stojí, že BASH_VERSINFO je pole, které má 6 položek (0 až 5). Obsahuje jednotlivé komponenty označení verze Bashe. Např. pod BASH_VERSINFO[2] se skrývá patch level, hodnotu celého pole zobrazíme přirozeně příkazem echo ${BASH_VERSINFO[*]}.

PS1 - syntaxe promptu

Co to je prompt, jsme si řekli už v prvním díle. Tehdy jsem také slíbil, že vám jednou ukážu, jak jej změnit. Ten čas právě nadešel. Prompt je skupina znaků, kterou v interpretru vídáme asi nejčastěji. Spokojeně si hoví před našim kurzorem a cosi se nám snaží sdělit.

Prompt v mém systému mi říká, který uživatel je v daném Bashi přihlášen, na kterém běží Bash počítači (to se hodí, když jsem v různých terminálech přes ssh přihlášen k různým počítačům) a také ve kterém stojím adresáři. Pokud se vám to nelíbí, máte štěstí, protože používáte svobodný software.

V případě Bashe je konfigurace promptu hračkou (alespoň pro toho, kdo četl pozorně minulý díl seriálu). Syntaxe promptu je totiž definována v proměnné - konkrétně v proměnné PS1. Zkusil jsem proto napsat echo $PS1 a vypadlo [\u@\h \W]\$.

To není chyba, tak to má skutečně být. První znak je hranatá závorka, která se mi v promptu objevuje hned na začátku. Následuje \u, což je aktuální uživatel. Zavináč se opět přímo vypíše a za ním \h - tj. jméno počítače. Následuje mezera a \W - aktuální adresář (jen jeho nejnižší úroveň). Pak už jen formální uzavření hranaté závorky a místo ostrého hrotu znak dolaru, za nějž se postaví kurzor.

Než začneme zkoušet, uschovejme si původní hodnotu promptu: PSold=$PS1. Nejprve malá recese: PS1="C:/>". Protože konfigurujeme Bash změnou hodnoty proměnné, úprava se projeví okamžitě. Tak se nelekněte. Zkusme něco originálnějšího: PS1="\d, \A \u@\h \w > ". Některé konstrukce využitelné při stavbě vlastního promptu uvádím na okraji.

Konstrukce promptu:

Jak vidno, dá se s tím vyřádit do sytosti. Komu by to nestačilo, tak podotýkám, že součástí definice může být jiná proměnná! Doporučuji pak dát definici do apostrofů. Např. PS1='\u@\h $PATH > '. Zpátky k normálu se vrátíme pomocí PS1=$PSold. Je to váš nástroj na orání systému, takže si jej nabruste dle libosti.

Závěr

Tentokrát to byla poctivá tvrdá polnohospodářská práce. Přeji hodně úspěchů a zábavy s úrodou nových vědomostí. Příště se podíváme na standardní vstupy a výstupy, jejich přesměrovávání a další možnosti komunikace mezi procesy.