Skriptování v Bournově shellu/Řízení běhu

Z Wikiknih
Přejít na: navigace, hledání
◄ Skriptování v Bournově shellu/Expanze proměnných Řízení běhu Skriptování v Bournově shellu/Soubory a proudy ►

Zatím jsme probrali základy a teorii. Probrali jsme výběr různých shellů a jak v Bournově shellu pouštět skripty. Mluvili jsme o unixovém prostředí a ukázali jsme si, jak používat proměnné k uchovávání hodnot. Nedošlo ovšem ještě na to, abychom doopravdy něco udělali, abychom přiměli systém proskočit kruhy, donést noviny nebo umýt nádobí.

Teď je na čase, abychom se do toho opřeli. V této kapitole dojde na programování, budeme mluvit o tom, jak se skripty rozhodují a jak pouští příkazy. Budeme probírat řízení běhu a vykonávání příkazů.

Řízení běhu[editovat]

Co je ten rozdíl mezi spouštěčem programů a příkazovým shellem? Proč je Bournův shell nástrojem, který má moc a uznání, nejen hloupou pomůckou k spouštění skutečných programů. Je to proto, protože Bournův shell není jen prostředím k spouštění programů, Bournův shell je plně programovatelným prostředím se silou plnohodnotného programovacího jazyka. V kapitole o prostředí jsme si ukázali, že Bournův shell má k dispozici proměnné. Tím ovšem jeho možnosti nekončí. Stejně jako běžný programovací jazyk i Bournův shell má možnosti pro větvení programu a pro opakování příkazů.

Test: vyhodnocování podmínek[editovat]

Abychom mohli učinit rozhodnutí, musíme mít způsob, jak vyhodnocovat podmínky. Musíme být schopni rozhodnout se o stavu, na základě kterého se má skript větvit.

Poměrně překvapivě samotný Bournův shell k tomu žádný nástroj nemá. Je zde standardní nástroj zvaný test, který byl napsán pro potřeby shellového skriptování, nicméně přísně vzato není tento nástroj součástí samotného shellu. Test vyhodnotí zadanou podmínku a na základě svého zjištění vrací hodnotu pravda nebo nepravda. Vracení probíhá pomocí shellové proměnné $? (návratové hodnoty) a pravda má podobu nuly, zatímco nepravda je nenulová. Obecná podoba volání příkazu test je:

test podmínka

konkrétním příkladem může být srovnání rovnosti řetězců

test "Ahoj, Franto" = "Ahoj, Franto"

jehož návratová hodnota bude nula (pravda), neboť řetězce jsou si opravdu rovné.

V shellových skriptech ale častěji uvidíte alias v podobě hranatých závorek [, jehož syntaxe je obecně

[ podmínka ]

a pro náš předchozí příklad srovnávání řetězců má podobu:

[ "Ahoj, Franto" = "Ahoj, Franto" ]

Jedná se opravdu jen o přezdívku pro příkaz test, umí vyhodnotit právě ty podmínky, které lze vyhodnotit pomocí něj. Povšimněme si mezer, které oddělují závorky od obsahu. Ty jsou velmi důležité a jejich opomenutí patří mezi časté (nejen) začátečnické chyby.

Příkaz test umí vyhodnotit pěknou řádku různých podmínek. Na některých systémech jich může být více, my si nicméně uvedeme ty základní:

Testování souborů[editovat]

-b soubor
soubor existuje a jedná se o speciální blokový soubor
-c soubor
soubor existuje a jedná se o speciální znakový soubor
-d soubor
soubor existuje a jedná se o adresář
-f soubor
soubor existuje a jedná se o obyčejný datový soubor
-g soubor
soubor existuje a má nastavený SGID bit
-k soubor
soubor existuje a má nastavený sticky bit
-p soubor
soubor existuje a jedná se o pojmenovanou rouru
-r soubor
soubor existuje a je čitelný
-s soubor
soubor existuje a má nenulovou velikost
-t [n]
Otevřený soubor číslo n je terminálovým zařízením (n je nepovinné, přednastavené na 1)
-u soubor
soubor existuje a má nastavený SUID bit
-w soubor
soubor existuje a je zapisovatelný
-x soubor
soubor existuje a je spustitelný

Testování řetězců[editovat]

-n s
s má nenulovou délku
-z s
s má nulovou délku
s0 = s1
s0 a s1 jsou shodné
s0 != s1
s0 a s1 jsou rozdílné
s
s je neprázdné

Testování celočíselných podmínek[editovat]

n0 -eq n1
n0 je rovné n1
n0 -ge n1
n0 je větší nebo rovno n1
n0 -gt n1
n0 je ostře větší než n1
n0 -le n1
n0 je menší nebo rovno n1
n0 -lt n1
n0 je ostře menší než n1
n0 -ne n1
n0 není rovno n1

Skládání podmínek[editovat]

Z výše vyčtených podmínek je navíc možné skládat složené podmínky pomocí následujících syntaxí:

\(B\)
Kulaté závorky se zpětnými lomítky slouží k seskupení podmínek. Podmínka \(B\) je pravdivá právě tehdy, když podmínka B.
! B
Negace, je pravdivá právě kdy B je nepravdivá.
B0 -a B1
Logický součin, výsledná podmínka je pravdivá, pokud jsou B0 i B1 zároveň pravdivé.
B0 -o B1
Logický součet, výsledná podmínka je pravdivá, pokud je alespoň jedna z podmínek B0 a B1 pravdivá.

Podmíněné provádění[editovat]

Teď, když umíme vyhodnocovat podmínky, můžeme tuto znalost použít k řízení běhu programu. Základními stavebními kameny programovacích jazyků je možnost podmíněného provádění příkazů a možnost opakovaného provádění příkazů, neboli cykly. K cyklům se dostaneme později, teď se budeme soustředi na podmíněné provádění. To Bournův shell podporuje hned dvěma příkazy, příkazem if a příkazem case.

Příkaz if je z těch dvou obecnější a jeho obecná podoba je následující:

if seznam příkazů
then seznam příkazů
elif seznam příkazů
then seznam příkazů
…
else seznam příkazů
fi

Provádění tohoto příkazu lze shrnout následovně:

  1. Nejprve jsou provedeny příkazy v seznamu za ifem
  2. Pokud měl poslední z příkazů za ifem návratovou hodnotu nula (pravda), pak jsou provedeny příkazy ze seznamu za prvním then a tím je vykonávání příkazu if skončeno
  3. Pokud měl poslední z příkazů za ifem nenulovou hodnotu (nepravda), pak jsou proveden příkazy ze seznamu za prvním elifem (je-li tam)
  4. Pokud měl poslední z příkazů za oním elifem nulovou návratovou hodnotu (pravda), pak jsou provedeny příkazy za následujícím then a tím je vykonávání příkazu if skončeno
  5. Pokud měl poslední z příkazů za oním elifem nenulovou návratovou hodnotu (neprava), pak se pokračuje stejným způsobem následujícím elifem (je-li tam)
  6. Pokud ani za ifem ani za jedním z elifů nebyl poslední příkaz s návratovou hodnotou nula, pak jsou provedeny příkazy za else (je-li tam). Tím je vykonávání příkazu if ukončeno.

Stojí za povšimnutí, že za každým z klíčových slov příkazu if je možné použít celý seznam příkazů, jak v podmínkách, tak ve vykonávaných větvích. Výsledek je vždy závislý na posledním příkazu ze seznamu.

Ve většině případů ale v zájmu přehlednosti a spravovatelnosti programu nebudete chtít v podmínkových částech použít víc než jediný příkaz. Nejčastěji se bude jednat o výše zmíněný příkaz test. Ukažme si to na příkladu:

if [ 1 -gt 0 ]
then
  echo ANO
fi

Vypíše

ANO

protože 1 je skutečně ostře větší než 0.

Při použití i větve else je příklad o něco složitější:

if [ 1 -le 0 ]
then
  echo ANO
else
  echo NE
fi

Vypíše

NE

protože jedna není menší nebo rovno nule.

A ještě složenější příklad

if [ "$strana" = S ]
then
	echo sever
elif [ "$strana" = V ]
then 
	echo východ
elif [ "$strana" = J ]
then
	echo jih
elif [ "$strana" = Z ]
then
	echo západ
else
	echo nevím
fi

který nás přivádí k příkazu case. Příkaz case je svým způsobem zvláštním případem příkazu if, zejména vhodným v případě srovnávání hodnoty s mnoha předdefinovanými. Typickým použitím je zpracování přepínačů, které skript dostane na příkazovém řádku. Takových přepínačů je omezené množství a příkaz case je pro jejich zpracování ideální.

Ve své obecné formě má příkaz podobu:

case hodnota in
vzor0) seznam_prikazu;;
vzor1) seznam_prikazu;;esac

Hodnota může mít jakoukoliv podobu, může být například daná hodnotou proměnné prostředí. Každý z vzorů je regulárním výrazem a seznam příkazů je proveden pro první ze vzorů, který sedí na zadanou hodnotu (další nikoliv, takže pozor na překryv vzorů). Každý ze seznamů příkazů musí být ukončen dvěma středníky. Návratovová hodnota je nula, pokud příkaz skončí bez syntaktických chyb.

Složitý příklad větvení řešení výše pomocí elifů vypadá s pomocí casu takto:

case $strana in
	S) echo sever;;
	V) echo východ;;
	J) echo jih;;
	Z) echo západ;;
	*) echo nevím;;
esac
Kdy používat if a kdy case?[editovat]

Kdy se tedy má použít if a kdy case? Proč nabízí Bournův shell dva příkazy, jejichž smysl je tak podobný? Odpověď, technicky vzato, je jednoduchá: case se řídí hodnotami proměnných a dodanými regulárními vzory, zatím if se řídí návratový hodnotami programů (je tedy obecnější). Ukažme si příklad s oběma konstrukcemi:

#!/bin/sh
if [ "$2" ]
then
	veta="$1 je "
else
	echo "Nedostatek parametrů!" >&2
	exit 1
fi

case $2 in
	ovoce

Jak je vidět z prvního řádku, jedná se o shellový skript. Své parametry si načítá z příkazové řádky pomocí číslovaných proměnných. Začíná ifem, kterým ověřuje, že druhý parametr je nenulový, tedy, že jsou parametry alespoň dva. K tomu používá 'test'. Pokud jsou parametry alespoň dva, začne budovat větu, pokud je jich méně, vypíše chybové hlášení (na chybový výstup, podrobněji vizte v kapitole Soubory a proudy) a ukončí se s nenulovou návratovou hodnotou. Povšimněte si, že větev else má v tomto příkladě vícečlenný seznam příkazů.

Pokud skript nebyl ukončen, následuje case. V něm se otestuje hodnota parametru $2, kterou by měl být druh přijímané potravy. Na jeho základě se vypíš, že se jedná o býložravce, všežravce a nebo, pokud vstup neodpovídá ani jednomu z předchozích vzorů, o všežravce.

Vložme kód do souboru nazvaného 'potrava.sh' a zkusme si ho spustit s různými parametry:

sh potrava.sh
Nedostatek parametrů!
sh potrava.sh Pepa
Pepa je všežravec!
sh potrava.sh Pepa ovoce
Pepa je býložravec!
sh potrava.sh Pepa maso
Pepa je masožravec!

Opakování[editovat]

Kromě možnosti větvení programu nabízí každý programovací jazyk také možnost příkaz opakovat. Bournův shell má pro opakování hned tři řídící příkazy: while, until a for.

Cyklus while[editovat]

Příkaz while je nejpřímočařejčí podobou opakování v Bournově shellu a také podobou nejobecnější. Vypadá takto:

while seznam-příkazů
do seznam-příkazů2
done

Což má význam:

  1. Proveď příkazy z prvního seznamu příkazů.
  2. Pokud poslední příkaz z tohoto seznamu skončí s nenulovou návratovou hodnotou, končí celý příkaz while
  3. Jinak proveď příkazy z druhého seznamu a vrať se na krok 1.

Pokud příkaz neobsahuje syntaktické chyby a pokud skončí, má návratovou hodnotu nula.

Stejně jako příkaz if, i příkaz while nabízí možnost vzít jako řídící celý seznam příkazů, ale stejně jako u if záleží jen na posledním z nich. Ve skutečnosti se většinou používá jediný příkaz a obvykle se opět jedná o příkaz 'test'.

Následující cyklus vypíše čísla od 0 do 10 (což lze také na UN*Xech provést standardním příkazem 'seq').

citac=0

while [ $citac -lt 10 ]
do
  echo $citac
  citac=`expr $citac + 1`
done

Jiný příklad, tentokrát bližší reálnému skriptování. Příkaz while se používá například pro zpracování (předem neznámého) počtu parametrů z příkazového řádku, v kombinaci s příkazem shift a speciální proměnnou '$#', která určuje počet parametrů.

#!/bin/sh

while [ $# -gt 0 ]
do
  echo $1
  shift
done

Cyklus until[editovat]

Příkaz until funguje skoro stejně jako příkaz while, pouze interpretuje svoji podmínku opačně. K opakování dochází, pokud má poslední příkaz z řídícího seznamu nenulovou návratovou hodnotu. K opakování tedy dochází, pokud podmínka není splněna. Obecná podoba vypadá stejně jako u while:

until ''seznam příkazů''
do ''seznam příkazů''
done

Následující příklad ukazuje čekání, dokud soubor muj_soubor nebude mít přesně 10000 řádků.

until [ $radek -eq 10000 ]
do
  radky=`wc -l muj_soubor`
  sleep 5
done

Cyklus for[editovat]

V části věnované rozdílu mezi if a case jsme si všimli, že if závisí na návratových hodnotách, zatímco case závisí na datech. Podobně se to má s cykly while a until, které závisí na návratových hodnotách, a s cyklem for, který závisí na datech.

Cyklus totiž proběhne pro seznam hodnot, které jsou mu na počátku zadány a v to následující podobě:

for promenna in s₁ s₂ … 
do
  seznam příkazů
done

Seznam příkazů je proveden pro každý řetězec ze seznamu následujícího po in, přičemž patřná hodnota je vždy k disposici pod jménem zadaným na v ukázce jako promenna. Před do musí být odřádkování nebo středník, stejně tak musí být středník nebo odřádkování před done. Ukažme si příklad:

for jmeno in pondělí úterý středa čtvrtek pátek sobotu neděli
do 
  echo Na $jmeno si nic neplánuji, budu se celý den flákat.
done
Na pondělí si nic neplánuji, budu se celý den flákat.
Na úterý si nic neplánuji, budu se celý den flákat.
Na středa si nic neplánuji, budu se celý den flákat.
Na čtvrtek si nic neplánuji, budu se celý den flákat.
Na pátek si nic neplánuji, budu se celý den flákat.
Na sobotu si nic neplánuji, budu se celý den flákat.
Na neděli si nic neplánuji, budu se celý den flákat.

Cyklus for se také často užívá pro zpracování parametrů skriptu, dokonce je to tak, že pokud bude seznam za in prázdný, doplní tam shell $*.

Spustíme-li skript

#!/bin/sh
for arg
do 
  echo $arg
done

s parametry

sh skript.sh A B C D

vypíše

A
B
C
D

Vykonávání příkazů[editovat]

Ukázali jsme si příkazy větvení programu a příkazy na cykly. Teď se podíváme na další syntaktické možnosti Bournova shellu související s pouštěním příkazů.

Spojování příkazů[editovat]

Kromě příkazu if nabízí Bournův shell ještě jeden způsob, jak podmínit vykonávání příkazu návratovou hodnotou příkazu předcházejícího. Jsou to operátory && a ||. Možná je znáte ve významu logického součtu a logického součinu z programovacího jazyka C.

Operátor && spojí dva příkazy v tom smyslu, že druhý je proveden pouze pokud první skončí s nulovou návratovou hodnotou („uspěje“). Například

[ -f soubor ] && rm soubor

Tedy příkazem test otestujeme, zda existuje soubor soubor, a pokud ano, smažeme jej.

Operátor || spojí dva příkazy naopak tak, že druhý je proveden pouze pokud první skončí s nenulovou návratovou hodnotou („neuspěje“). Například

test -f soubor

Tedy příkaz test otestuje, zda existuje soubor soubor, a pokud ne, vytvoří jej (prázdný příkazem touch).

Oba tyto operátory mohou být vnímány jako hůře čitelné než použití ifu. A u obou je návratovou hodnotovou návratová hodnota posledního z provedených příkazů.

Seskupování příkazů[editovat]

Více příkazů je možné seskupit do seznamu jejich oddělením operátorem ;, například můžeme vytvořit adresář a vlézt do něj:

mkdir muj_adresar; cd muj_adresar

V tomto případě jsou oba příkazy provedeny nezávisle na návratové hodnotě prvního.

Vymezení seznmu příkazů pro přehlednost či pro nějaký zvláštní účel je možné dvěma způsoby, složenými závorkami a kulatými závorkami. Složené závorky ve skutečnosti nemají žádný zvláštní význam, nicméně středník musíte vložit i za poslední prvek seznamu. Vypadá to tedy například takto:

{ mkdir muj_adresar; cd muj_adresar; }

Vymezení kulatými závorkami je zajímavější. Pokud totiž vložíte seznam příkazů do kulatých závorek, je tento seznam proveden ve zvláštním, novém procesu. To znamená zejména to, že změny prostředí, které jsou v tomto seznamu provedeny, neovlivní původní prostředí, z kterého byl seznam spuštěn. Zkusme si to předvést úpravou předchozího příkladu:

(mkdir muj_adresar; cd muj_adresar)

Opět dojde k vytvoření adresáře, dokonce do něj vstoupíme, ovšem jen v rámci podřazeného procesu. Po návratu do rodičovského procesu budeme opět v adresáři, kde předtím.

Další příklad, tentokrát s proměnnými:

PROM0=A
(PROM1=B)
echo \"$PROM0\" \"PROM1\"
A

Proměnná PROM1 byla vytvořena v podřazeném procesu, v původním procesu nastavena nebyla a tak v něm není nastavena ani po návratu z podřazeného procesu.

Příkazové nahrazování[editovat]

V kapitole o prostředí jsme si ukázali, jak nahrazovat proměnné jejich hodnotami. Bournův shell ale nabízí také příkazové nahrazování, kdy je příkaz nahrazen svým výstupem. Ve skutečnosti jsme si to ukázali už na příkladu výše u cyklu while, kde jsme dosazovali výsledek aritmetické operace do proměnné prostředí.

Pro nahrazení příkazu jeho výstupem se používají dva zápisy. Původní Bournův shell používal zpětné apostrofy (`příkaz`), což je syntaxe dodnes podporovaná většinou shellů. Standard POSIX 1003.1 později přidal možnost zápisu $( příkaz ). Ukažme si to na příkladech:

cp soubor zalohy/soubor-`date`
cp soubor zalohy/soubor-$(date)

Regulární výrazy a metaznaky[editovat]

Při práci shellem chcete obvykle dát jednoznačným způsobem najevo, s jakými soubory má příkaz pracovat. Často se ale bude jednat o celou skupinu souborů, které mají nějakou společnou vlastnost, třeba jsou ve stejném adresáři, mají stejnou příponu, nebo obojí. Nebo chceme mít určitou volnost při rozpoznávání vzorů v příkaze case, jak jsme si ukázali výše.

Pro tyto případy nabízí Bournův shell (omezenou) podporu regulárních výrazů: vzorů, které umožňují vyjádřit nějakou skupinu textových řetězců s určitými společnými vlastnostmi. Je možné je použít prakticky kdekoliv (ovšem jen někde to dává smysl). Regulární výrazy se vytvářejí pomocí následujících metaznaků:

*
Jakýkoliv řetězec.
?
Jakýkoliv jednotlivý znak.
[znaky]
Jeden ze znaků uvedených v hranatých závorkách.
[!znaky]
Jeden znak jiný než uvedené v hranatých závorkách.
vzorO|vzor1
(pouze pro použití v case) Jeden ze vzorů vzor0 nebo vzor1.

Ukažme si pár příkladů s regulárními výrazy:

  • Vypsání všech souborů s koncovkou ".dat"
ls *.dat
  • Vypsání všech souborů, jejichž jména začínají "file-", pokračují dvěma libovolnými znaky následovanými koncovkou ".txt"
ls file-??.txt
  • Zálohování všech textových souborů na kopie s datem v jméně
for i in *.txt; do cp $i backup/$i-`date +%Y%m%d`; done
  • Vypsání všech souborů v adresářích zalohy0 a zalohy1
ls zaloha[01]
  • Vypasání všech souborů v jiných adresářích záloh
ls zalohy[!01]
  • Spuštění všech shellových skriptů, jejichž jména začínají "muj_skript" a končí příponou ".sh"
muj_skript*.sh

Regulární výrazy a skryté soubory[editovat]

Soubory, jejichž jméno začíná tečkou, jsou považovány za speciální. Říká se jim skryté a metaznaky na úvodní tečku nesepnou. Jedná se o jednoduchý způsob částečné ochrany před tím, aby s nimi uživatel něco provedl omylem. Pokud chceme provést nějaký příkaz se skrytými soubory, musíme úvodní tečku doopravdy napsat. Příkaz pro vypsání souborů začínajících tečkou vypadá následovně:

ls .*

a vypíše třeba

.
..
.profile

což by mohla být podoba chudého uživatelského domovského adresáře. Vidíme zde soubor '.profile', konfigurační soubor Bournova shellu, a speciální adresáře '.' (aneb „tento adresář”) a '..' (aneb „nadřazený adresář”). S těmito přezdívkami současného adresáře a nadřazeného adresáře můžete pracovat jako s jakýmikoliv jinými jmény souborů, například

ls .

vypíše obsah aktuální adresáře a

cd ..

změní pracovní adresář na nadřazený.

Uvozovkování[editovat]

Jakmile se začnou používat metaznaky, které mají speciální význam, objevuje se přirozená otázka, jakým způsobem je možné tyto znaky použít v jejich běžném významu, jak to udělat, aby s nimi nedělal shell své triky. Předpokládejme třeba soubor, který má ve jméně znak *, tedy hvězdičku. Jakým způsobem na takový soubor odkázat? Zkusíme třeba

echo POKUS0 > hvezdickovy*.soubor
echo POKUS1 > hvezdickovyHVEZDA.soubor
cat hvezdickovy*.soubor

a dostane se nám odpovědi

POKUS0
POKUS1

Což pravděpodobně není to, co bychom chtěli. Naštěstí jsou způsoby, jak potlačit interpretaci metaznaků. V shellu k tomu lze použít dvojité a jednoduché uvozovky a také znak zpětného lomítka. A lze je použít i k tomu, aby potlačily roli mezery coby oddělovače. Jejich použití je:

\
Zpětné lomítko slouží k potlačení zvláštního významu jediného (následujícího) metaznaku
''
Jednoduché uvozovky plně potlačují všechny metaznaky, které jsou v jimi uzavřeném řetězci
""
Dvojité uvozovky potlačují většinu metaznaků, které jsou do nich uzavřeny, ovšem některé metaznaky si svůj speciální význam zachovají i v dvojitých uvozovkách.

Nejjednodušším způsobem uvozovkování je tedy zpětné lomítko, které zruší zvláštní chování jediného znaku. Například zatímco

echo *

vypíše třeba

soubor1 soubor2 soubor3

tak

echo \*

vypíše

*

Za zvláštní zmínku stojí, že zpětné lomítko takto funguje i na znak odřádkování, takže lze pomocí zpětného lomítka rozdělit příkaz na několik řádků:

echo Tohle je opravdu\
hodně dlouhý příkaz!
Tohle je opravdu
hodně dlouhý příkaz!

Také funguje na znak mezery, například:

ls soubor s mezerou.txt

může mít za následek jen chybová hlášení:

ls: cannot access soubor: No such file or directory
ls: cannot access s: No such file or directory
ls: cannot access mezerou.txt: No such file or directory

zatímco

ls soubor\ s\ mezerou.txt

vypíše dle očekávání

soubor s mezerou.txt

A konečně zmiňme, že zpětné lomítko funguje i samo na sebe. Chcete-li zpětné lomítko bez jeho zvláštních schopností, použijte na něj … zpětné lomítko. Tedy \\.

Jak jsme si ukázali na příkladu s mezerami, můžeme zpětné lomítko v jednom příkaze použít několikrát. Bournův shell ovšem nabízí i pohodlnější možnost, jak potlačit speciální význam většího počtu znaků: uvozovky. Ty jednoduché fungují jednoduše, veškeré do nich uzavřené znaky (kromě jednoduché uvozovky, pochopitelně) pozbývají své zvláštní významy.

echo '**************'
**************

To funguje podle očekávání. Často se ovšem stává, že součástí výstupu je nějaká proměnná, nebo výstup příkazu. Pro takové příkazy jsou dvojité uvozovky. Ty potlačují speciální význam znaků s výjimkou:

  • dvojitých uvozovek (ty jsou potřeba k ukončení uvozovek)
  • znaku $, tedy vypisování proměnných
  • znaku `, tedy interpretování výstupu programu
◄ Skriptování v Bournově shellu/Expanze proměnných Řízení běhu Skriptování v Bournově shellu/Soubory a proudy ►