Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

Hrátky z řádky: první krůčky při programování v Bashi

V dnešním díle se podíváme na základy práce s proměnnými a příkazy pro řízení běhu skriptu (if-then-else, for, while). Bash se pro vás tak nepopiratelně stane plnohodnotným programovacím jazykem. Jak však bylo možná vidět i v dřívějších ukázkách, na řadě míst vás drobný překlep snadno dostane do velkých potíží.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

Skripty jsou interpretovány doslova řádek po řádku, takže např. neošetřená chyba na jednom znamená „jen“ to, že se problematický řádek přeskočí a pokračuje se dalším. Sotva kdy ale v takhle nepředpokládané situaci budou následující příkazy dělat to, co jste zamýšleli. Navíc skripty z principu spoléhají na celou řadu příliš slabě kontrolovaných entit: že je správně nastavená proměnná $PATH, že jsou k dispozici takové verze programů, které znají přepínače, jaké používáte. (Pověstný je v tomto směru rozdíl mezi Solarisem a jinými unixy jako je např. Linux. Standardní programy jako sort se sice jmenují stejně, ale chovají se pokaždé malinko jinak.)

Při programování v bashi buďte proto velmi opatrní, testujte vše a nevěřte ničemu. A nepište žádné velké skripty…

Co je to skript a jak se liší od „programu“

Skript se od „plnohodnotného programu“ liší jen dvěma drobnostmi: skript je textový soubor čitelný i prostému lidskému oku, kdežto „program“ je rovnou kompilován a instrukce v něm přímo čte procesor. Druhá drobnost je ta, že skript ke spuštění potřebuje interpret, tj. program, který skript zpracovává a „chová se podle něj“. Jinak se skript a program neliší, pro oba platí to, co jsme psali o vstupech, výstupech, návratových hodnotách a podobně v dřívějším dílu.

Z obyčejného textového souboru uděláme skript tak, že mu prostě nastavíte příznak spustitelnosti:

chmod +x muj_skript

Dobrým zvykem ovšem je napsat na první řádek skriptu, který interpret se má použít (jinak se použije aktuální shell). Formulka zvaná familiérně hashbang ( #!) pro bash vypadá typicky takto:

#!/bin/bash

Mimochodem právě hashbang je rozhodující znak pro utilitku file; buď váš skript prohlásí za anglický text v ASCII, nebo za skript pro bash.

Stejný hashbang (ovšem s jiným interpretem) se užívá pro všechny skriptovací jazyky, a až v bashi zvládnete příkazy read a select, bude pro vás hračka napsat si v bashi interpret vlastního jazyka…

Proměnné interní a proměnné prostředí

Jedním ze základních stavebních kamenů každého programovacího jazyka jsou proměnné, úložné místo pro mezivýsledky výpočtu. Bash je současně trošku programovací jazyk a současně nástroj pro řízení prostředí Unixu. Proto u proměnných odlišuje dva základní stavy: proměnná pouze interní a proměnná „exportovaná“ mezi další proměnné prostředí. Exportované proměnné mohou číst i programy, které skript spustí. Dobrým zvykem je pojmenovávat proměnné prostředí jen velkými písmeny a interní proměnné naopak malými písmeny, závazné to ale není.

Hodnotu proměnné nastavíte rovnítkem, případné mezery je třeba ochránit, jak jsme si již říkali dříve. Exportovat proměnnou můžete buď samostatným příkazem export nebo export a přiřazení spojit:

promenna=slovo_1" a druha cast"; export promenna
# nebo
export promenna=hodnota

V bashi je navíc možnost exportovat proměnné jen pro jeden příkaz těsně před jeho spuštěním:

prom1=hod1 prom2=hod2 prikaz
# po skončení prikazu už prom1, ani prom2 definovány nejsou

Operace s proměnnými

Na několika příkladech ukážu základní „úpravy“ hodnot proměnných. Místo podrobného komentáře ale znovu zdůrazním, proč byste své programování v bashi měli omezit na minimum:

  • Každá mezera v hodnotě proměnné a každá nečekaně prázdná proměnná vás kousne, pokud proměnnou byť jedinkrát zapomenete obalit uvozovkami.
  • Aritmetika v bashi je jen celočíselná, hodí se tak na jednoduché čítače.
  • Proměnné se hodí jen na poměrně krátké řetězce, větší data udržujte raději vždy v pomocných souborech (a tam je zase problém se soupeřením skriptů, ale o tom někdy jindy).
  • Běh bashových skriptů je pomalý a nákladný na systémové zdroje, skoro každý příkaz znamená spuštění nějakého programu.

A teď ty náměty pro vaši fantazii. Zabezpečení proti všemu nečekanému nechávám na vás (např. cokoli jiného než číslo v $i), stejně jako některé další možnosti:

i=$(($i+1)) # $(()) je "aritmetická expanze", užili jsme ji na inkrementaci čítače
fn=$dir/file$i.gz # spojení řetězců -- prostě napište řetězce za sebe
soub=${fn//file/soubor} # náhrada všech výskytů řetězce "file" v hodnotě proměnné $fn
ungzfn=${fn/.gz/} # nebezpečné odstranění .gz, odstraní první výskyt, nikoli nutně příponu
ungzfn=${fn/%.gz/} # bezpečné odstranění přípony .gz, odstraní jen na konci řetězce
unfilefn=${fn#$dir/file} # bezpečné odstranění předpony "$dir/file", odstraní jen na začátku řetězce
surefn=${fn:-nahradni_soubor} # hodnota proměnné $fn, nebo náhradní hodnota, není-li $fn definováno
surefn=${fn:=nahradni_soubor} # hodnota proměnné $fn, nebo náhradní hodnota, není-li $fn definováno, navíc *nastaví i proměnnou fn*

Řízení běhu – větvení

Řízení běhu (větvení a cykly) je v bashi založeno na návratových hodnotách programů, viz dřívější díl. Zde je příklad větvení:

logf=muj_log.txt
if grep Failed $logf; then \
  echo "Můj log už obsahuje signál chyby."; \
elif grep Succeeded $logf; then \
  echo "Už máme úspěch!"; \
else \
  echo "Zatím se nic neví"; \
fi

Středníky a zpětná lomítka na koncích řádků uvádím pro zdůraznění, že bash musí dostat celý if-then-else-fi najednou. Pokud větvení píšete do skriptu nebo na příkazovou řádku, můžete „ \“ před koncem řádky vynechat – bash ví, že něco musí následovat, a načte další kousek skriptu nebo vás promptem poprosí o pokračování. Pokud však větvení bashi dáváte nějak nepřímo (např. v Makefilu, v definici aliasu atp.), středníky potřebujete a „ \<newline>“ je zavedená opisná sekvence pro „tady řádek vlastně nekončí“.

Povšimněte si, že za then, elif a else středník není (nesmí být), před nimi naopak musí.

Řadu ifů lze někdy nahradit matchováním výrazů; takhle např. můžete detekovat, kde je váš notebook (zpětná lomítka na konci řádek opět jen proto, abyste se nebáli případně řádky spojit hned za sebe):

case $(/sbin/ifconfig | grep -A1 ^eth0 | sed -n 's/^.*inet addr:\([^ ]*\).*/\1/p') in \
195.113.*) echo škola ;;  \
10.*|192.168.*) echo doma ;; \
*) echo nevím ;; \
esac

Povšimněte si roury jako oddělovače jednotlivých vzorů, kulaté závorky jako oddělovače vzoru od příkazů a (povinného) dvojitého středníku pro ukončení každé varianty. Bash provede první odpovídající variantu a ostatní přeskočí. Pro případ, že žádný vzor neodpovídá, jsme připravili závěrečné „ *)“; bez něj by celý case neudělal nic.

Řízení běhu – cykly

Dvě ukázky for-cyklu:

for f in *.gz; do gunzip < $f > ${fn/%.gz/}; done
# ke každému zabalenému souboru vyrobíme i rozbalený
# běžný gunzip *.gz by originály smazal

for i in nula jedna `seq -w 2 6` sedm; do echo $i; done
# legrační počítání, vypíše: nula jedna 2 3 4 5 6 sedm

A dvě ukázky while:

# aktivní čekání, až příkaz ping uspěje
while ! ping -c 1 www.ja-sam.cz; do sleep 2; done; echo "Server naběhl"

# Takto konzoli změníte na užitečného interaktivního pomocníka:
# Ukončete pomocí Ctrl-D
prompt="Zadej doménu: "; echo -n $prompt; \
while read dom; do whois $dom; echo -n $prompt; done; echo "Konec"

Příkaz testu

Zatím umíme větvení a cykly založit např. na grepu. Při kapce invence by vás napadlo použít

cat soubor >/dev/null 2>&1

jako nákladný test existence souboru a hrozivé echo-echo-diff pro rovnost proměnných. Naštěstí existují lepší alternativy: základní „ [“ a vylepšený „ [[“ test. Základní test má chudší repertoár operátorů a je zachován pro zpětnou kompatibilitu.

Příkaz testu (a nadále se držme vylepšeného) je příkaz jako každý jiný, jen se jmenuje divně „ [[“ a navíc vyžaduje, aby poslední jeho argument byl řetězec „ ]]“. Navenek to vypadá, že se testy píší do dvojitých hranatých závorek; fakt, že jde o obyčejný příkaz a jeho argument, však vyžaduje mezery kolem závorek (a přesný počet argumentů uvnitř).

Často užívané operátory a obraty si představíme v příkladech:

[[ x$a == x ]] && echo "proměnná a je prázdná" # protože když jí předřadíte x, dostanete zas jen x
[[ -z $a ]] && echo "jiný test na prázdnost"
[[ -e soubor ]] && echo "soubor existuje"
[[ -d adr ]] && echo "adr existuje a je adresář"
[[ 0 == 0.0 ]] && echo "řetězec 0 je shodný s 0.0"  # není shodný, samozřejmě
[[ 50 -eq 050 ]] && echo "číslo 50 je sice rovno 050, ale můj bash to nepozná, je váš lepší?"  # celočíselná rovnost
[[ 215 < 23 ]] && echo "v abecední pořadí platí: 215<23"
[[ 215 -lt 23 ]] && echo "tohle nenastane, v celočíselném uspořádání neplatí: 215<23"

Ve výše uvedené ukázce while jste si možná všimli vykřičníku před příkazem ping pro negaci (booleovského výkladu) návratové hodnoty. Stejně můžete negovat výsledek příkazu testu, a příkaz testu navíc akceptuje vykřičník i uvnitř:

# následující dva příkazy jsou ekvivalentní
[[ ! -z $a ]]
! [[ -z $a ]]
[[ -n $a ]] # a tohle je třetí možnost testu neprázdnosti

Kombinace testů (a jiných příkazů) je samozřejmě možná pomocí || a &&, závorkování pomocí složených závorek. Dejte pozor na povinný středník před zavírací složenou závorkou:

if ! [[ -e s1 ]] || { [[ ! -e s2 ]] && grep a s3; } ; then echo x; fi

A tím pro dnešek už opravdu ukončíme.

Školení: GIT správce zdrojových kódů

 

Seznamte se s možnosti systému správy verzí zdrojových kódů GIT, který používají i vývojáři linuxového jádra.

  • Proč správa verzí
  • Architektura GITu
  • GIT v praxi
  • a další

Podrobnější informace o školení a přihláška

Ohodnoťte jako ve škole:
Průměrná známka 2,96

Přehled názorů

bash's cool
Harvie 17. 3. 2008 00:19
Nový
chyby mohou být konec
Tomas Z. 17. 3. 2008 00:55
Nový
dalsi zdroje
mibo 17. 3. 2008 01:03
Nový
vylepseny test
shadowrunner 17. 3. 2008 01:37
Nový
└ 
Re: vylepseny test
anonymní uživatel 17. 3. 2008 09:14
Nový
Advanced Bash-Scripting Guide
Ladislav Hagara 17. 3. 2008 02:11
Nový
a jejda
anonymní uživatel 17. 3. 2008 04:26
Nový
├ 
Re: a jejda
anonymní uživatel 17. 3. 2008 08:57
Nový
├ 
Re: a jejda
gld 17. 3. 2008 09:56
Nový
│
└ 
Re: a jejda
Ondřej Bojar 18. 3. 2008 08:41
Nový
│
 
└ 
Re: a jejda
Petr Macek 18. 3. 2008 11:52
Nový
└ 
Re: a jejda
peca 17. 3. 2008 10:18
Nový
 
└ 
Re: a jejda
Ash 17. 3. 2008 16:24
Nový
[[ 40 -eq 050 ]]
Václav Kocian 17. 3. 2008 08:58
Nový
Kopirovani bin. soub. v BASHi
Mir a B. 17. 3. 2008 10:55
Nový
├ 
Re: Kopirovani bin. soub. v BASHi
anonymní uživatel 17. 3. 2008 11:01
Nový
├ 
Re: Kopirovani bin. soub. v BASHi
Harvie 17. 3. 2008 14:41
Nový
│
└ 
Re: Kopirovani bin. soub. v BASHi
Harvie 17. 3. 2008 15:01
Nový
│
 
└ 
Re: Kopirovani bin. soub. v BASHi
Harvie 17. 3. 2008 17:08
Nový
├ 
Re: Kopirovani bin. soub. v BASHi
aaa 17. 3. 2008 16:37
Nový
│
└ 
Re: Kopirovani bin. soub. v BASHi
matej 18. 3. 2008 18:29
Nový
└ 
Re: Kopirovani bin. soub. v BASHi
pht 23. 3. 2008 10:09
Nový
aritmetická expanze
anonymní uživatel 17. 3. 2008 13:19
Nový
└ 
Re: aritmetická expanze
pht 23. 3. 2008 10:09
Nový
ndiswrapper ze skriptu
Eugene 19. 3. 2008 12:08
Nový
└ 
Re: ndiswrapper ze skriptu
Ash 19. 3. 2008 22:37
Nový
 
└ 
Re: ndiswrapper ze skriptu
Eugene 27. 3. 2008 09:51
Nový
Detekce připojení notebooku
Kit 26. 4. 2008 16:50
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem