11Kapitola

Bash 11: if, then, fi, else, elif

V tomto díle budeme pokračovat ve větvení skriptu. Slíbené velké opakování jsem se rozhodl nechat až na příště. Navážeme na minulou část, kde jsme se seznámili s podmínkami. Představíme si podmínkový příkaz if a různé způsoby jeho použití. To nám umožní lépe rozlišovat, které části skriptu budou provedeny při výskytu určitých okolností. Opakování to ale přece jen do značné míry bude, protože if dělá v podstatě stejnou práci jako konstrukce, které jsme probírali minule. Bohdan Milar .

Základní konstrukce if, then, fi

Celá konstrukce if je zakončena slůvkem fi, což nepředstavuje nic jiného než if napsané obráceně. Jak uvidíme později, není to jediný případ takto tvořeného názvu příkazu v Bashi.

Již dříve jsme si předvedli, jak v bashovém skriptu nechat provést jinou operaci, pokud nějaký příkaz dopadne pozitivně, a jinou, když se nezdaří. Nebo podmínit provedení určitého příkazu existencí nějakého souboru, hodnotou proměnné nebo výsledkem výrazu. Příkaz if nám dává možnost provést za daných okolností celou skupinu příkazů.

O provedení dalších příkazů rozhoduje if na základě návratového kódu příkazů zadaných mu pro rozhodování. Trochu se to podobá konstrukci $příkaz1 && příkaz2$, kterou známe z 10. dílu. Nejjednodušší syntaxe if má podobu $if příkaz1; then příkaz2; fi$.

Činnost && jsme si ukázali na výpisu obsahu adresáře home. Použili jsme tehdy zápis $ls home && echo ':-)'$. U stejného příkladu zůstaneme i dnes, ale s if:

$ if ls /home ; then echo ':-)' ; fi
bohdan  pavel  petr
:-)
$ if ls /mohe ; then echo ':-)' ; fi
ls: /mohe: není souborem ani adresářem

Vidíme, že se if chová zcela stejně jako &&, a to v případě vyhovění i nevyhovění sledované podmínce. Podobně jako v případě ampersandů se také if používá nejčastěji společně s příkazem test.

Použití testu v if

Pro zopakování uvedu, že test vrací (stejně jako jiné příkazy) návratový kód 0, pokud vše proběhlo v pořádku, tj. vyhodnocovaná podmínka je platná. Hodnotu 1 vrací tehdy, když podmínka neplatí. Také připomínám, že s příkazem test se můžeme velmi často setkat v podobě otevírací hranaté závorky.

Že test umí hodnotit typ (např. běžný soubor, adresář, odkaz, ...), práva, vlastníka a další parametry souboru, také víme. Seznam testovatelných charakteristik a příslušné parametry jsou shrnuty v tabulce. Najdete v ní také možnost porovnávání stáří souborů dle času poslední změny a operátory pro práci s proměnnými (řetězci) a výrazy. Ve spojení s ampersandy jsme si ukázali tento zápis:

$ [ -e /etc/passwd ] && echo "DB uživatelů existuje"

Stejná situace s if bude vypadat následovně:

$ if [ -e /etc/passwd ] ; then echo "DB uživatelů existuje" ; fi

Z dosud uvedených příkladů je vidět, že zápis pomocí if je delší než s použitím &&. Proč si tedy komplikovat život a zbytečně natahovat skripty? Nejde o komplikaci, ale o správnou volbu prostředků.

Složitější konstrukce

Zatímco konstrukce && (a ||) jsou určeny k provedení jednoho nebo několika málo podmíněných příkazů, if slouží k tvorbě složitých konstrukcí, vnořených podmínek apod. && a || využijete spíše na příkazové řádce, if asi častěji ve skriptech. Volba je na vás. Ukažme si krátký příklad použití if ve skriptu. Připomínám, že způsobu tvorby skriptů (abyste si mohli uvedené příklady sami zkoušet) jsme se věnovali v 8. díle tohoto seriálu.

Pokud byste chtěli použít if jako náhradu dvojitých svislítek, tj. ve smyslu pokud neplatí, stačí před hodnocenou podmínku umístit vykřičník. Např. if ! [ -e /etc/passwd ] ; then ...

if [ -e /etc/passwd ] ; then
echo "DB uživatelů existuje"
echo "Kteřípak používají Bash?"
grep "/bin/bash$" /etc/passwd
fi

Pokud existuje soubor $etcpasswd$, vypíší se hlášky na druhém a třetím řádku. Následně příkaz grep na vybere z $etcpasswd$ všechny řádky končící řetězcem $binbash$.

 

Jinak - else

No jo, namítnete možná, ale co konstrukce$příkaz1 && příkaz2 || příkaz3$, která provede příkaz2 v případě platnosti podmínky a příkaz3, když neplatí? Na tuto možnost samozřejmě if pamatuje také. Využívá k tomu pomocný příkaz else.

Jako příklad použijeme zase hledání v $etcpasswd$ pomocí grep, ale jeho výstup přesměrujeme do černé díry (/dev/null). Zajímá nás jen návratový kód, tedy zda takové řádky existují. Pokud ano, vypíšeme (pomocí programu awk) pouze jména příslušných uživatelů. Pokud ne, vypíše se (pomocí echo), že nikdo:

if grep "/bin/bash$" /etc/passwd > /dev/null ; then
echo "Bash mají nastaveni:"
awk -F ":" '$7 == "/bin/bash" {print $1}' /etc/passwd
else
echo "Nikdo :-("
fi

Noříme se hlouběji

Tak a teď ke vnořeným podmínkám. To je možnost, kde má if oproti dvojitým ampersandům a rourám jednoznačně navrch. V následujícím výpise propojíme oba výše uvedené příklady:

if [ -e /etc/passwd ] ; then
echo "DB uživatelů existuje"
echo "Kteří pak používají Bash?"
if grep "/bin/bash$" /etc/passwd > /dev/null ; then
echo "Bash mají nastaveni:"
awk -F ":" '$7 == "/bin/bash" {print $1}' /etc/passwd
else
echo "Nikdo :-("
fi
fi

Povšimněte si, že příkazy uzavřené dovnitř podmínky odsazuji o několik znaků (resp. o tabulátor). Příkazy uzavřené do vnořené podmínky ještě více atd. Je to dobrý zvyk, který značně zpřehledňuje delší skripty. Patří to do programátorské etiky podobně jako komentáře ve zdrojovém kódu.

Ještě jinak - elif

Další věcí, kterou má if oproti jiným rozhodovacím mechanismům navíc, je element elif. Ten umožňuje postupné rozhodování v průběhu jedné konstrukce. Jak lze z jeho názvu vyvodit, je to něco mezi else a if. Přesněji řečeno: pokud neplatí podmínka hodnocená přímo v if, lze se rozhodovat ještě na základě jiného kritéria.

Za příklad vezměme opět počet příznivců Bashe mezi uživateli lokálního systému. Pokud se mezi nimi nenajde nikdo, nebude je hned odsuzovat. Podíváme se ještě, zda náhodou někdo z nich nemá přednastavenu odlehčenou verzi zvanou sh:

if grep "/bin/bash$" /etc/passwd > /dev/null ; then
echo "Bash mají nastaveni:"
awk -F ":" '$7 == "/bin/bash" {print $1}' /etc/passwd
elif grep "/bin/sh$" /etc/passwd > /dev/null ; then
echo "Bash sice nikdo, ale tito mají sh:"
awk -F ":" '$7 == "/bin/sh" {print $1}' /etc/passwd
else
echo "Nikdo :-("
fi

Závěr

Tak nakonec ani tento díl nebyl tak úplně oddechový. Příští už ale opravdu opakovací bude. Už si na vás připravuji skript, do kterého zakomponuji co nejvíce probraných věcí.

-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.
soubor1 -nt soubor2     Pravda, když je soubor1 novější než soubor2.
soubor1 -ot soubor2     Pravda, když je soubor1 starší než soubor2.
soubor1 -ef soubor2     Pravda, když soubor1 a soubor2 mají shodný inode na stejném disku.
-z řetězec              Pravda, když je řetězec prázdný.
-n řetězec              Pravda, když je délka řetězce nenulová.
řetězec1 = řetězec2     Pravda, když řetězce jsou stejné.
řetězec1 != řetězec2    Pravda, když řetězce nejsou stejné.
! výraz                 Pravda, když výraz je nepravdivý.
výraz1 -a výraz2        Pravda, když jak výraz1 tak výraz2 jsou pravdivé.
výraz1 -o výraz2        Pravda, když je aspoň jeden z výrazů výraz1 nebo výraz2 pravdivý.