Dnes si ukážeme podmínky. Je to další programátorská konstrukce, která umožňuje větvení programu, resp. skriptu, který jsme se již v tomto seriálu naučili vytvářet (viz 8. díl). Podmínky nám umožňují provedení určité části skriptu pouze v případě, kdy nastanou určité okolnosti.
Návratový kód
Aby program (skript) mohl být něčím víc, než jen seznamem po sobě prováděných příkazů, musejí si tyto být schopny mezi sebou předávat nějaké zprávy. K tomu se hodí proměnné, o kterých jsem už také psal (viz 4. a 5. díl). Bash používá jednu takovou s názvem PIPESTATUS a ukládá do ní zprávu o výsledku práce posledního provedeného příkazu. Tato zpráva má podobu jednobytového čísla (0-255) a nazývá se návratový kód.
Zpravidla se nástroje GNU a interní příkazy Bashe chovají tak, že návratový kód 0 znamená úspěšné provedení operace a nenulový návratový kód chybu. V příkladu se budeme snažit vypsat obsah adresářů - jednoho existujícího a jednoho neexistujícího. Znak $ zastupuje prompt:
$ ls /home bohdan pavel petr $ echo $PIPESTATUS 0 $ ls /mohe ls: /mohe: není souborem ani adresářem $ echo $PIPESTATUS 1
Konstrukce || a &&
Znaky | a & nebývají běžně na českých klávesnicích dostupné. Řešení je přepnutí klávesnice na anglickou. Pokud se vám to ale nechce dělat, máte i další možnosti. V textových (virtuálních) konzolích lze těchto znaků dosáhnout pomocí levého Altu a ASCII kódu zadaného přes numerickou klávesnici. Pro & je to Alt+38, pro | Alt+124. V prostředí X (např. KDE) lze využít pravý Alt a klávesy alfanumerické části klávesnice - pro & Alt+7, pro | Alt+w.
S ampersandem (znak &) jsme se seznámili už ve 2. díle, kdy jsme s jeho pomocí spouštěli úlohy na pozadí. Se svislítkem (znak |) jsme zase v 6. díle vytvářeli nepojmenované roury. Pokud ale najdete ve skriptu tyto znaky zdvojené, znamená to něco úplně jiného - jde o jednoduché podmínkové konstrukce. Před dvojicí znaků i za ní se nachází příkaz. Druhý příkaz se však provede v závislosti na hodnotě návratového kódu příkazu prvního.
Význam konstrukcí && a || lze odvodit také z formální logiky, kde && představuje logické AND (tj. příkaz1 a současně příkaz2). || je pak ztvárněním logického OR, tedy příkaz1 nebo příkaz2.
příkaz1 && příkaz2 - příkaz2 bude proveden pouze v případě, že příkaz1 skončí s návratovým kódem 0.
To znamená, že příkaz2 se provede, pokud příkaz1 skončí úspěchem. V následujícím příkladu si necháme v závislosti na výsledku operace zobrazit veselou emotikonu:
$ ls /home && echo ':-)' bohdan pavel petr :-) $ ls /mohe && echo ':-)' ls: /mohe: není souborem ani adresářem
V prvním případě proběhl příkaz správně, a proto se emotikona zobrazila. Ve druhém případě tomu tak nebylo. A nyní ke svislítkům:
příkaz1 || příkaz2 - příkaz2 bude proveden pouze v případě, že příkaz1 skončí s návratovým kódem různým od nuly. Druhý příkaz tedy bude proveden za předpokladu, že ten první skončí nezdarem. Vyzkoušíme stejný příklad, avšak v obráceném gardu a se smutnou emotikonkou:
$ ls /home || echo ':-(' bohdan pavel petr $ ls /mohe || echo ':-(' ls: /mohe: není souborem ani adresářem :-(
Emotikona se ukázala ve druhém případě, kdy je název adresáře zadán chybně. Mohli bychom chtít, aby nám emotikona nahradila (nikoli doplnila) chybovou hlášku, a proto by bylo vhodné se jí zbavit. K tomu nám poslouží přesměrování výstupu (viz 6. díl):
$ ls /mohe 2>/dev/null || echo ':-(' :-(
Obě konstrukce můžeme kombinovat na jednom řádku. Takže třeba pokud skončí zdárně příkaz1, provede se příkaz2, pokud ne, provede se příkaz3. V našem případě by to vypadalo následovně:
$ ls /home 2>/dev/null && echo ':-)' || echo ':-(' deti milarb petr test :-) $ ls /mohe 2>/dev/null && echo ':-)' || echo ':-(' :-(
Nutno podotknout, že příkaz1 může mít mezi svými argumenty proměnné, takže jeho výsledek není na první pohled tak zřejmý jako ve výše uvedených příkladech. Na závěr si proto ukážeme kraťoučký skript, který ve smyčce for (viz 9. díl) přiděluje proměnné ADRESAR různé hodnoty. Ty se pak příkaz vyzdobený konstrukcemi && a || pokusí vypsat.
for ADRESAR in home opt user var; do echo "Adresář /${ADRESAR}:" ls /${ADRESAR} 2>/dev/null && echo ':-)' || echo ':-(' echo done
Testy a hranaté závorky
Zatím jsme si předvedli, že v bashovém skriptu můžeme nechat provést jinou operaci, pokud nějaký příkaz dopadne pozitivně, a jinou, když se nezdaří. Co ale kritéria jako existence určitého souboru, hodnota proměnné nebo výsledek výrazu? I ty lze pro větvení použít, a to pomocí stejného mechanismu - návratové hodnoty. Existuje totiž specializovaný příkaz pro vyhodnocování těchto situací a jmenuje se test.
Test vrací (stejně jako jiné příkazy) hodnotu 0, pokud vše proběhlo v pořádku, tj. vyhodnocovaná podmínka je platná. Hodnotu 1 vrací tehdy, když podmínka neplatí. Vzniká tak trochu paradoxní situace, kdy pro Bash je 0 pravda a 1 nepravda. S tím si ale nemusíme příliš lámat hlavu. Dobré může být vědět, že když dojde k chybě (např. špatná syntaxe), vrací test návratový kód 2.
Ukažme si funkci příkazu test na příkladu. Budeme zjišťovat existenci souboru /etc/passwd. K tomu u příkazu test slouží přepínač -e. Výsledek ověříme vypsáním hodnoty proměnné PIPESTATUS. Potom zkusíme totéž se souborem /var/passwd, který většinou neexistuje:
$ test -e /etc/passwd $ echo $PIPESTATUS 0 $ test -e /var/passwd $ echo $PIPESTATUS 1
Co se týká hodnocení souborů, nemusíme zůstávat jen u jejich existence. Testovat lze typ (např. běžný soubor, adresář, odkaz, ...), práva, vlastníka apod. Zde je seznam:
-b soubor - existuje a je to blokový speciální soubor;
-c soubor - existuje a je to znakový speciální soubor;
-d soubor - existuje a je to adresář;
-e soubor - existuje;
-f soubor - existuje a je to normální soubor;
-g soubor - existuje a má právo set-group-id;
-k soubor - existuje a má nastavený sticky bit;
-L soubor - existuje a je to symbolický odkaz;
-p soubor - existuje a je to pojmenovaná roura (FIFO);
-r soubor - existuje a je čitelný;
-s soubor - existuje a má délku větší než nula;
-S soubor - existuje a je to soket;
-u soubor - existuje a má nastaven set-user-id bit;
-w soubor - existuje a je zapisovatelný;
-x soubor - existuje a je proveditelný;
-O soubor - existuje a je vlastněný efektivním user id;
-G soubor - existuje a je vlastněný efektivním group id.
S příkazem test se můžeme velmi často setkat v dosti zvláštní podobě - otevírací hranaté závorky. Pokud voláme test jako závorku, musíme ji po vypsání hodnocené podmínky zase uzavřít. Za otevírací i před uzavírací závorkou doporučuji dělat mezeru. Příklad:
$ [ -e /etc/passwd ]
Ve spojení s ampersandy to může vypadat následovně:
$ [ -e /etc/passwd ] && echo "DB uživatelů existuje"
Obdobně dobře je test vybaven i pro práci s proměnnými (řetězci) a výrazy. Nejčastější použití je asi při srovnání hodnoty proměnné s nějakým řetězcem. Např chceme zjistit, zda se proměnná licence rovná řetězci "GPL": [ "$licence" = "GPL" ]. Všimněte si, že kolem rovnítka jsou mezery. Kdyby tam nebyly, šlo by o přiřazení hodnoty, nikoli o porovnávání. Obě porovnávané hodnoty také doporučuji uzavírat do uvozovek. Vyhneme se tak chybné interpretaci prázdných řetězců a řetězců s více slovy.
Pokud chcete zjistit, zda se řetězce nerovnají, stačí těsně před rovnítko přidat vykřičník ([ "$licence" != "GPL" ]). Když vás zajímá, zda proměnná existuje a není prázdná, stačí zadat [ "$licence" ]. Chcete-li testovat, zda neexistuje (resp. je prázdná), pomůže vám [ -z "$licence" ].
Pro zpřehlednění skupin podmínek a stanovení jejich priorit lze využít kulatých závorek, podobně jako v matematických rovnicích. K negování celého výrazu v rámci testovaných podmínek před něj stačí předřadit vykřičník.
Test dokáže s podmínkami provádět také logické operace. Logickému A (AND) odpovídá přepínač -a. Chceme-li smazat soubor jen tehdy, když máme práva zápisu a proměnná SMAZAT má hodnotu ANO, zadáme [ -w soubor -a "$SMAZAT" = "ANO" ] && rm soubor. K logickému NEBO (OR) se analogicky používá přepínač -o.
Závěr
Tento díl byl trochu hutnější. Jsem si toho vědom a doufám, že nikoho z pravidelných čtenářů neodradil. Těm nepravidelným doporučuji přečíst si i předcházející díly. Všem však mohu slíbit, že příští díl bude více opakovací. Ke zopakování dosud probrané látky využijeme ukázkového skriptu.