12Kapitola

Bash 12: Opakování

Bohdan Milar sklízí úrodu a vyzkouší vás ze všeho, co vás naučil.

Minule jsem přislíbil nerozvádět nová témata, ale pozastavit se nad již probranou látkou a znázornit ji na delším skriptu. Projdeme si v něm vše od proměnných, rour a smyček až po podmínky. Celý skript je uveden ve výpisu.

#!/bin/bash
SKUP=/etc/group
UZIV=/etc/passwd
[ -r $SKUP -a -r $UZIV ] || exit 2
echo -e "\nSeznam deseti vybraných skupin"
echo -e "\nGID\tskupina"
echo "--------------------"
for g in $( cut -d ":" -f 1 $SKUP | tail -n 10 | sort ) ; do
cislo=$( grep $g $SKUP | cut -d ":" -f 3 )
echo -e "$cislo \t$g"
done
echo
sleep 2
echo
echo "A nyní abecedně seřazený seznam uživatelů:"
echo
seznam=""
for u in $( cut -d ":" -f 1 $UZIV | sort ) ; do
seznam="$seznam $u"
done
echo $seznam
echo
exit 0

První řádek uvozuje skript pro Bash. Další dva (prázdné řádky nepočítáme, jsou jen pro zpřehlednění) definují hodnoty proměnných SKUP a UZIV. Nenechte se splést. Hodnotou budou řetězce /etc/group a /etc/passwd, nikoli např. obsah těchto souborů.

Soubor /etc/passwd obsahuje informace o účtu uživatele, nikoliv hesla. Soubor /etc/group zase definuje skupiny, do kterých uživatelé patří. Oba soubory může měnit jenom root, a to ještě přesně definovaným postupem. Špatným zápisem do souboru si můžete celý systém nenávratně poškodit, protože uživatelé jsou i programy, nejen lidé.

V dalším řádku testujeme (příkaz test, resp. "["), zda jsou oba (-a) soubory (tj. cíl cesty uvedené v řetězci obou proměnných) přístupné pro čtení (-r). Název proměnné se prostě při vykonávání příkazu nahradí jeho hodnotou. Pokud čitelné nejsou (||), bude skript ukončen s návratovou hodnotou 2 (exit 2). Pokud byla podmínka splněna, pokračujeme dál ke třem řádkům echo. První dva z nich používají přepínač -e, který umožní využívat speciálních znaků. V našem případě jsou to znaky \n (nový řádek) a \t (horizontální tabulátor). Tabulátor využijeme k zarovnávání sloupců. Poslední z trojice echo pak vykreslí řadu pomlček. Vznikne tak nadpis a záhlaví tabulky.

Dostáváme se k nejsložitějšímu řádku skriptu. Již na první pohled je zřejmé, že jde o začátek smyčky for-do-done. Proměnná g bude v jejím průběhu nabývat hodnot, které vygeneruje posloupnost příkazů uzavřená v kulatých závorkách. Prvním příkazem v závoce je cut. Jde o klasický GNU nástroj pro vyřezávání částí řádků textových souborů. Volbou -d ":" mu říkáme, že jednotlivá pole v řádcích souboru jsou oddělena znakem dvojtečka. Volba -f 1 pak znamená, že chceme vypsat hodnotu prvního pole. Vstupním souborem je hodnota proměnné $SKUP, tedy /etc/group.



Za normálních okolností by takto zadaný cut vypsal hodnoty všech prvních polí souboru /etc/group, tedy seznam všech skupin definovaných v systému pod sebou. My ale pomocí | přesměrováváme tento výstup ke zpracování příkazu tail. Ten slouží k výpisu "ocásku" (tj. posledních několika řádků) vstupního souboru (tedy výstupu z cut). Délka ocásku je stanovena pomocí -n 10 na deset řádků. Výsledkem příkazu tail bude seznam jmen posledních deseti skupin ze souboru /etc/group. Ani to nebude finální, protože za tail se objevuje další roura. Mezivýsledek tak poputuje ještě do třídičky sort. Ta se postará o to, že náš seznam deseti vyvolených (skupin) bude (abecedně) seřazen.

Takže výsledkem celé závorky v řádku for g ... bude abecedně seřazený seznam jmen deseti posledních skupin ze souboru /etc/group. Proměnná g pak při každém průchodu cyklem nabyde postupně hodnotu jednoho z nich. Tak a co vlastně děje uvnitř cyklu?

Nejprve si naplníme proměnnou cislo. Ta bude opět nabývat hodnotu toho, co vypadne z příkazů seřazených v závorce. Prvním je grep, který vypíše ze zadaného souboru pouze ty řádky zadaného souboru (zde $SKUP, tedy /etc/group), ve kterých se vyskytuje zadaný řetězec.

Hledaným řetězcem je zde hodnota proměnné $g, tedy jméno skupiny zpracovávané v tom kterém průchodu smyčkou. Protože by se v souboru /etc/group nemělo nacházet více skupin stejného jména, měl by být vypsán právě jeden řádek.

Vypsaný řádek je opět s použitím roury přenesen na standardní vstup příkazu cut. O jeho funkcionalitě jsme se již zmiňovali, proto jen shrnu, že z dodaného řádku souboru /etc/group vyřízne třetí pole, tj. číslo hledané skupiny. To se stane pro tento průchod smyčkou hodnotou proměnné cislo.

Na dalším řádku vypíšeme pomocí echo vedle sebe aktuální (při každém průchodu smyčkou budou jiné) hodnoty proměnných $cislo a $g. Díky přepínači -e můžeme opět použít tabulátor (\t), aby se sloupečky zarovnávaly hezky pod příslušné nadpisy v hlavičce.

A jsme na konci první smyčky, protože vidíme "patník" done. Od ní se skript bude vracet k for g ... tak dlouho, dokud bude g nabývat nových hodnot (tj. do deseti). Po posledním průchodu bude pokračovat dál a dostane se k echo. To bez parametrů udělá prostě prázdný řádek.

Nyní přichází "odpočívadlo". Aby si pomalý uživatel mohl tabulky alespoň rychle přelétnout očima. Skript si zde na dlouhé dvě sekundy (tj. několik miliard procesorových cyklů) odpočine (sleep 2). Pokračovat bude vynecháním dalšího řádku (echo), zobrazením nadpisu další části a vynecháním ještě jednoho řádku. Když přidělíme proměnné hodnotu "", znamená to, že ji vlastně vyprazdňujeme. Hodnoty proměnných lze totiž uzavírat do uvozovek, a pokud uvedeme dvě uvozovky těsně vedle sebe (tj. není v nich nic, ani mezera), nabývá proměnná hodnotu nic, tj. nijakou, tedy žádnou, takže je prázdná. A právě to jsme udělali s proměnnou seznam.

A propracovali jsme se k další smyčce for-do-done. Tentokrát budeme naplňovat proměnnou u a opět výsledkem příkazů uzavřených do závorek. Nám již dobře známé cut vyřízne ze souboru /etc/passwd rozděleného na pole dvojtečkami (-d ":") pole první (-f 1), tj. jméno uživatele.

Výsledek této řezničiny, jak už je u mne zvykem, poputuje rourou. Na její druhé straně čeká netrpělivě sort, aby příchozí řetězce (jména uživatelů systému) setřídil. Smyčka se tedy bude opakovat tolikrát, kolik uživatelů je v daném systému definováno (kolik řádků má soubor /etc/passwd).

Vnitřek druhé smyčky je velmi skromný. Definujeme hodnotu proměnné seznam tak, aby na konci obsahovala jména všech uživatellů oddělená mezerou. Kdybychom zadali seznam=$u, tak by se v každém průchodu smyčkou přemazala novou hodnotou. Chceme-li hodnotu přidat, řekneme Bashi, aby se nová hodnota rovnala staré hodnotě ($seznam), jedné mezeře a hodnotě proměnné $u. Tímto způsobem si v každém průchodu smyčkou ponechá seznam svou hodnotu z minula (na začátku byl prázdný) a rozšíří se o aktuální hodnotu proměnné $u (tj. další jméno v pořadí). Nyní můžeme smyčku ukončit pomocí done. Následuje prosté vypsání hodnoty proměnné seznam, jeden obligátní prázdný řádek a řádné ukončení skriptu pomocí exit 0.

Image

Závěr

Tento skript může na první pohled vypadat děsivě. Věřím však, že pro pozorné čtenáře seriálu není po mikroskopické analýze ničím nepochopitelným. Většina skriptů nebývá tak zamotaná jako tento. Abyste ale rozuměli čemukoli, na co můžete narazit, máme před sebou ještě řádný kus práce. Příště se podíváme na další typy cyklů, které Bash vedle for-do-done nabízí.