cd $HOME cp soubor.txt soubor.zaloha1.txt cp soubor.txt soubor.zaloha2.txt rm soubor.txt echo soubor.txt byl zalohovan a nasledne smazanAby tento příklad mohl fungovat, je nejjednodušší vytvořit nový soubor, např.
mujskript.sh
(Přípona není důležitá jako v DOSu. Systém je inteligentní a rozpoznává
soubory podle obsahu. Klidně můžete příponu vynechat.)
#!/bin/bashTuto řádku budete psát na začátek všech vašich skriptů. Informujete tím shell, že má jako interpretr spustit
/bin/bash
.
Dále už můžete psát příkazy, které chcete, aby skript provedl. Je důležité, aby
byl každý příkaz na samostatném řádku. Pokud chcete mermomocí umístit na řádek
více příkazů, musíte je oddělit středníky:
mount /floppy; mv archiv.tgz /floppy; umount /floppyNaopak pokud je příkaz moc dlouhý, můžete jej rozdělit na více řádků znakem
\
cp /home/student/inf98/sommerm/.xsession \ /home/student/inf98/sommerm/.xsession-zaloha-dne-10.4.2000
Soubor může obsahovat prázdné řádky pro zvýšení čitelnosti. Pokud někde
uvedete znak #
, vše za tímto znakem, až do konce řádku, se
ignoruje (využívá se pro komentáře).
Až skončíte s editací souboru, nezapomeňte označit skript jako spustitelný
(chmod 755 mujskript.sh
). No a nyní ho spustíte jako jakýkoliv
jiný program:
./mujskript.shDávejte si obzvlášť dobrý pozor na to, kde se skript vykonává. Pokud obsahuje nebezpečné příkazy (
rm, mv, cat /dev/null > soubor
apod.)
a místo
v adresáři záloha je najednou v adresáři data, jenom proto, že proměnná
$zaloha_path byla vždy nadstavena "nějak sama", může to mít nedozírné následky.
promennaA=1 promennaB=$promennaA echo Hodnota druhe promenne je: $promennaBJak jistě tušíte, vypíše se:
Hodnota druhe promenne je: 1
Pokud potřebujete oddělit jméno proměnné od ostatního textu, stačí ji obklopit složenými závorkami
cesta=/home/ skupina=inf98 echo Seznam ucastniku ve skupine $skupina: ls ${cesta}student/$skupinaPříklad udělá to stejné jako
ls /home/student/inf98
.
Zde jsme museli
proměnné $cesta
a $skupina
spojit řetězcem
student/
(bez mezer!), protože jinak by
příkaz ls obdržel několik samostatných argumentů a došlo by k vypsání obsahu
adresáře /home/ a pravděpodobně chybové hlášení, že adresář student a inf98
neexistují.
Kdybychom jméno proměnné $cesta
neoddělili závorkami,
shell by si myslel, že se jedná o proměnnou $cestastudent
,
která však neexistuje, a předal by příkazu ls "nic"/inf98
,
což by vedlo na chybové hlášení, že /inf98 neexistuje.
Když jsme to už nakousli, tak se pojďme podívat na systémové proměnné. Tyto proměnné nastavuje shell v závislosti na konkrétní situaci. Pokud bychom náš příklad spustili následovně:
mujskript.sh parametr1 parametr2 parametr3 ... parametrnHlavní systémové proměnné by vypadaly takto:
$?
- obsahuje návratový kód posledního procesu spuštěného
na popředí.
$$
- obsahuje pid aktuálního procesu (ať víte co zabíjet :-)
$0
- obsahuje jméno právě prováděného skriptu:
mujskript.sh
$1
- obsahuje první argument, předaný vašemu skriptu,
na příkazové řádce:
parametr1
$9
- obsahuje 9-tý argument z příkazové řádky:
parametr9
$*
- obsahuje všechny argumenty volání programu (jako jedno slovo):
parametr1 parametr2 ... parametrn
$@
- obsahuje všechny argumenty volání programu (jednotlivá slova):
parametr1 parametr2 ... parametrn
$#
- obsahuje počet argumentů z příkazové řádky
(v našem případě n).
Pokud voláte skript s více než devíti argumenty, jsou nejvyšší (>9)
parametry
zatím nedostupné. Existuje příkaz shift
, který posune argumenty
o jedno dolů ($2 -> $1, $3 -> $2), takže desátý argument bude k dispozici
v proměnné $9 a první argument se ztratí.
Proměnná $# se automaticky zmenší o jedničku.
read promenna
.
Tímto se načte do proměnné promenna vše až do stisku klávesy enter.
read promenna1 promenna2 .... promennaNJestliže je proměnných méně než vkládaných slov, uloží se do poslední proměnné celý zbytek řádku.
Jak jste si možná všimli, výstup programu zajišťuje příkaz echo
.
(Pro podrobnější seznámení doporučuji podívat se na manuálovou stránku
echo
).
echo Ahoj, ja jsem bash skript a jmenuju se $0. Co jsi ty?V některých případech nebudete chtít, aby echo automaticky odřádkoval po ukončení výpisu (např. při otázce pro uživatele). Stačí jenom přidat parametr
-n
echo -n Zadej svuj plat: read plat echo Nevypadas jako bys vydelaval $plat dolaru.V souvislosti se vstupem a výstupem je vhodné se zmínit o různých uvozovkách a apostrofech. Tuto alchymii s řetězci byste měli ovládat, protože se rutinně používá, ale pro jistotu zde zopakujeme několik základních pravidel.
"ret"
Normální uvozovky. Řetězec uvnitř těchto uvozovek je
chápán shellem literárně (obyčejný text). To znamená, že různé metaznaky
jsou ignorovány (středník neznamená konec příkazu, ale prostě středník).
Do textu jsou vkládány hodnoty proměnných.
text=10; echo "text je $text;" # vytiskne: text je 10;
'ret'
Apostrof vedle klávesy enter. Řetězec uvnitř těchto
apostrofů je chápán shellem literárně (obyčejný text). Na rozdíl od
normálních uvozovek je navíc zamezeno nahrazování proměnných jejich
hodnotou.
text=10; echo 'text je $text;' # vytiskne: text je $text;
`ret`
Obrácené apostrofy. Řetězec je shellem chápán jako
příkaz k vykonání; Tento řetězec je vykonán před zpracováváním zbytku
řádku a výsledek příkazu nahradí původní řetězec `ret`
text=whoami; echo `$text;` # v mem pripade vytiskne: chickyNásleduje lepší příklad:
echo "V aktualnim adresari je `ls | wc -l` souboru"
test vyraz
,
pripadne [ vyraz ]
(mezera kolem závorek je povinná!).
V praxi se
používá hlavně druhý formát příkazu. Co vás může zarazit, že oproti běžným
zvyklostem nastavuje test
proměnnou $?
na hodnotu různou od nuly při nepravdivosti a na 0 při pravdivosti tvrzení.
A=7 [ "$A" -eq 7 ] && echo Cisla se rovnajiCo to je??? Takže popořádku. Nejprve do proměnné A uložíme hodnotu 7 (používáme ji poprvé, tudíž bez dolaru). Poté provedeme test výrazu
"$A" -eq 7
. Výraz se dá do češtiny přeložit jako
"rovná se proměnná $A
číslu 7?". Protože je to pravda, vrátí
test
této podmínky true a pokračuje se dalším příkazem
(echo Cisla se rovnaji
). Pokud by vrátil test
false, výraz vyraz AND vyraz by nikdy nebyl pravdivý,
tudíž se přeskočí vykonávání jeho pravé větve.
Více testovacích operátorů najdete v tabulce 1-4.
tab.1: souborové operátory | |
---|---|
[ -e soubor ] | soubor existuje |
[ -d soubor ] | soubor existuje a je to adresář |
[ -f soubor ] | soubor existuje a je to obyčejný soubor |
[ -L soubor ] | soubor existuje a je to symbolický link |
[ -s soubor ] | soubor existuje a má nenulovou velikost |
[ -r soubor ] | soubor existuje a dá se číst |
[ -w soubor ] | soubor existuje a dá se do něj zapisovat |
[ -x soubor ] | soubor existuje a je spustitelný |
[ f1 -nt f2 ] | soubor f1 je novější než soubor f2 |
[ f1 -ot f2 ] | soubor f1 je starší než soubor f2 |
tab.2: aritmetické operátory | |
---|---|
[ c1 -eq c2 ] | číslo c1 se rovná číslu c2 |
[ c1 -ne c2 ] | číslo c1 se nerovná číslu c2 |
[ c1 -gt c2 ] | číslo c1 je větší než číslo c2 |
[ c1 -ge c2 ] | číslo c1 je větší nebo rovno číslu c2 |
[ c1 -lt c2 ] | číslo c1 je menší než číslo c2 |
[ c1 -le c2 ] | číslo c1 je menší nebo rovno číslu c2 |
tab.3: řetězcové operátory | |
---|---|
[ -z ret ] | řetězec ret má nulovou velikost |
[ -n ret ] | řetězec ret má nenulovou velikost |
[ ret1 = ret2 ] | řetězec ret1 je shodný s řetězcem ret2 |
[ ret1 != ret2 ] | řetězec ret1 není shodný s řetězcem ret2 |
tab.4: logické operátory | |
---|---|
[ -a ] | spojí dvě podmínky (and) |
[ -o ] | spojí dvě podmínky (or) |
[ ! ] | neguje hodnotu následující podmínky |
[ \( \) ] | mezi tyto závorky můžete spojovat jednotlivé výrazy |
Pro vyhodnocování aritmetických výrazů se dá použít příkaz expr
,
který rozeznává operátory +, -, *, /, %
. Příkaz pracuje pouze
s celými čísly.
A=9 B=3 vysledek=`expr $A / $B + 1` echo Vysledek vyrazu '$A / $B + 1' je $vysledekPokud si spustíte následující příklad, vypíše se:
Vysledek vyrazu $A / $B + 1 je 4
if [ \( -r file1 \) -a \( ! -s file2 -o -d file2 \) ] then mv file1 file2 else echo Nebudu kopirovat. fiOops. Teď jsme se dostali trošku dál, než zatím umíme, ale zkuste si vyhodnotit podmínku mezi hranatými závorkami na prvním řádku.
Vidím, že je nejvyšší čas přejít k další kapitole.
if
.
Základní tvar příkazu je takovýto:
if [ podminka ] then prikazy else prikazy fiJak vidíte, příkaz má syntaxi velmi podobnou tomu co znáte z jiných programovacích jazyků. Všimněte si uzavíracího
fi
na konci příkazu. (Na toto začátečníci rádi zapomínají).
else
,
nebo naopak rozšířit na tvar elif
(else if):
if [ -e ${HOME}/.nexrc ]; then echo Cool. Pouzivate editor vi elif [ -e ${HOME}/.emacs ]; then echo Dalsi emacsista else echo Pouzivate neutralni editor echo Mel byste si konecne vybrat jedno nabozenstvi ';)' fiJak můžete vidět, slovo
then
je zvykem psát na řádek s
if
. Nesmí se však zapomenout na středník za podmínkou.
To samé platí i pro větve elif
- je to jen zkrácený zápis
else if
, tudíž musíte za podmínkou uvést klíčové slovo
then
.
Složené konstrukci if
je velmi podobný příkaz
case
, který použijeme, když testujeme jednu podmínku na
více hodnot:
case vyraz in vzorek1) prikazy;; vzorek2) prikazy;; esacNe, nepřepsal jsem se. Na konci každé sekce jsou opravdu dva středníky (aby se rozlišil konec sekce, pokud píšete více příkazů na jeden řádek).
vyraz
se všemi vzorekN
.
Vzorky mohou obsahovat metaznaky *, ?, [, ]
, které mají stejný
význam jako když specifikujete jméno souboru (pozor, toto nejsou regulární
výrazy!).
|
(znamená to, že stačí najít shodu pouze v jednom z
uvedených - logické or).
case $1 in -r) parametry=$1 echo Nastavil jsem parametr -r;; -v|-V) echo Parametry -v a -V zatim nic nedelaji;; *) echo Zadali jste neznamy parametr exit 1;; esacVýše uvedený kus kódu může být použit pro vyhodnocení prvního parametru z příkazové řádky ($1). Pro zpracování všech zadaných parametrů by se využil cyklus while a příkaz shift (viz. další část textu).
V dnešní době je nepředstavitelné, aby programátor nemohl opakovaně vykonávat zadanou sérii příkazů. Samozřejmě je možné okopírovat příkazy tolikrát, kolikrát je potřebujeme vykonat, ale stále to neřeší případ, kdy předem nevíme, kolikrát se příkazy mají opakovat (nehledě na estetickou stránku věci :). Proto zde máme několik možností:
while [ logicky_vyraz ] # while [ "$#" -ne 0 ] do # do prikazy # echo "parametr je $1"; shift done # doneSyntaxe příkazu je zhruba taková, jakou byste očekávali. Nicméně je zde ještě jedna podoba tohoto příkazu, na kterou si asi budete muset trochu zvykat. (unixovská filozofie)
# echo "parametr je $1" while prikaz # while shift do # do prikazy # echo "parametr je $1" done # donePraktický příklad vpravo má dělat to stejné jako předchozí ukázka, ale pozorný čtenář odhalí jeho slabinu: vytiskne se o jeden řádek více. (jestli vymyslím lepší příklad, určitě to updatuju.)
until
. Probíhá stejně jako
while
,
ale test podmínky musí být neúspěšný, aby se přistoupilo k další iteraci.
until [ logicky_vyraz ] # until [ "$#" -eq 0 ] do # do prikazy # echo "parametr je $1"; shift done # done
for
.
for promenna in seznam do prikazy donePosloupnost příkazů se postupně provádí pro všechny prvky seznamu. Tyto prvky jsou v seznamu odděleny mezerami nebo tabulátory. Pokud neuvedete část
in seznam
, provádí se cyklus nad seznamem parametrů,
se kterými byl skript vyvolán (proměnná $*
)
for i in 1 3 5 7 do echo $i doneVytiskne číslice 1, 3, 5, 7, každou na novém řádku. Síla tohoto příkazu spočívá ve způsobu, jakým se vytváří seznam, přes který se iteruje.
for i in `ls *.wav` do echo $i doneTento relativně jednoduchý příkaz vypíše všechny soubory s příponou wav v aktuálním adresáři, což není nic ohromujícího. Zaměníme-li však řádku
echo $i
třeba na
lame -h -b 192 $i ${HOME}/hudba/${i}.mp3provede skript to, že všechny wav soubory z aktuálního adresáře zkomprimuje do formátu mp3 pomocí enkodéru lame a výsledné mp3 soubory uloží do adresáře hudba ve vašem domovském adresáři.
Následující komplikovanější příklad všem uživatelům přihlášeným na tomto
počítači vypíše na konzoli text "Odhlas se, chci byt sam!". Veškerý chybový
výstup programu write
je přesměrován do černé díry
/dev/null
.
Podle návratové hodnoty příkazu write
je vypsáno jedno nebo
druhé hlášení.
for i in `users` do echo "Odhlas se, chci byt sam!"|write $i 2>/dev/null \ && echo $i dostal zpravu || \ echo zprava pro $i nemohla byt dorucena. doneSkript by chtělo ještě vylepšit, ale pro základní pochopení problému nám to stačí.
sleep
způsobí pozastavení provádění skriptu na dobu
zadanou v jednotkách [smhd] (sekundy, minuty, hodiny, dny)
i=80 echo "Nemame kam spechat, pockame $i sekund" while [ $i -gt 0 ] do sleep 1s echo -n "." i=`expr $i - 1` done echo "" echo "no tak budeme pokracovat, no."Ono to vypadá jako blbost, ale někdy se hodí s vykonáváním programu počkat (třeba aby si uživatel něco stihl přečíst, nebo pokud dáte programu nějaký čas a pokud do té doby neskončí, tak ho zabijete).
Pokud na konci příkazu uvedete znak ampersand &
, bude se
příkaz vykonávat na pozadí, tudíž se nebude čekat na jeho dokončení
a běh skriptu bude okamžitě pokračovat dále. To je výhodné, třeba když
nechcete čekat na zkopírování 600MB dat z jedné partition na jinou.
echo "Jenom tak budu kopirovat /dev/hda do /dev/null" cp /dev/hda /dev/null& echo "Zatim co mi to hrka s diskem, je cas na postu" mutt
V nějakém příkladu jsme narazili na řádku
prikaz1 && prikaz2
.
Kdo trochu programuje, tuší, že &&
je logická spojka
AND a v tomto konkrétním případě znamená, že prikaz2
se
provede jenom tehdy, pokud prikaz1
vrátí pravdivou hodnotu
(tj. 0).
Toto je velice častá konstrukce, která, spolu s kolegyní
prikaz1 || prikaz2
, umí pěkně zpřehlednit kód (místo testování
if prikaz1 ; then prikaz2
). Pěkné ukázky jsou ve startovacích
skriptech /etc/init.d/
cd /tmp || exit rm *.tmpPoslední příklad s || je výhodný, pokud budete následně v adresáři /tmp mazat způsobem
rm -rf *
. Bez tohoto testu by se sice vypsalo
chybové hlášení, že cd nemůže vstoupit do adresáře /tmp, ale ale další
příkazy, včetně onoho rm -rf *
, by se vykonaly v aktuálním
adresáři. S jednoduchým || testem se při nesplněné podmínce skript ukončí.
[ -f $HOME/.emacs ] && (echo "pouzivate OS emacs, nabootuju vam ho"; emacs)Když se tak dívám na předchozí příklad, napadá mě, že jsem použil jednu věc, kterou ještě neznáte: závorky. Nebudu to zde vysvětlovat (
man bash
). Zatím vám bude stačit vědět, že se používají k
seskupování příkazů. Kdybych je nepoužil, emacs by se spustil vždy, protože
vazba && se váže jenom na následující příkaz (echo
).
if [ vyraz ]
musí být testovaný výraz dodělen
mezerou od závorek [ ].
promenna=hodnota
nesmí být u rovnítka mezera
(na žádné straně)
man bash
,
případně se porozhlédnout po internetu. Kdo občas jezdí do ciziny,
může si v tamních knihkupectvích vybrat z přehršle zajímavých příruček.
Dobrou noc přeje chicky