Bash scripting
Co je to Bash?
Bash jako takový představuje v kontextu unixových operačních systémů (mezi které samozřejmě řadíme také LInux a jeho distribuce) tzv. shell. Jeho název pochází ze slovního spojení Bourne again shell. Od 70. let minulého století existoval tzv. Bourne shell pod zkratkou /bin/sh
v Unixu verze 7. Bash je jeho následovníkem. Zpět ale k shellu, udělejme si nyní krátkou odbočku do historie a vysvětleme si, co se pod tajuplným názvem shell skrývá.
Shell
Shell je jakýmsi nadřazeným pojmem pro Bash, proto je vhodné začít s jeho vysvětlením nejdříve. Ale vlastně to není zcela tak pravdivé. Jde o označení pro uživatelské rozhraní, nazýváme tak tedy způsob, jakým umožňujeme primárně uživateli komunikovat s linuxovým strojem a co víc -- hlavně jej ovládat a pomocí jisté syntaxe mu předávat příkazy, které pro nás má vykonat.
Shell, v doslovném překladu skořápka, je tedy abstraktním pojmem, který spouští právě určitý shell zpravidla po přihlášení uživatele do systému. A mezi nejčastější shell řadíme právě onen Bash, kterému se budeme v této kapitole věnovat.
Zpátky k Bashi
Nyní tedy víme, že Bash je jedním z mnoha shellů, s nimiž se můžeme setkat. Bash je prakticky prostředí, které si každý uživatel může libovolně nastavit dle své osobní preference. Setká se s ním při již zmiňovaném přihlášení do systému, při otevření terminálu, konzole, příkazového řádku...
Právě poslední zmíněný pojem je pro naše další pokračování podstatný. Příkazový řádek v terminálu/konzoli je jen a pouze jeden. Ale to, jakým způsobem jej můžeme ovládat, jak bude vypadat a chovat se, respektive poslouchat námi zadané příkazy, to je to, co nazýváme obecně shellem, v našem případě tedy Bashem.
Bash a jeho konkurence
Jak již bylo řečeno, Bash není jediným tzv. interpreterem příkazové řádky, existuje jich celá řada. Je na každém uživateli, který si zvolí, na internetu je jich plná řada. Jejich volba je zpravidla velice individuální a vedle vizuálního odlišení mohou mít také rozdílnou syntaxi, kterou následně překládají příkazové řádce do srozumitelné podoby.
I na Tvém stroji může být a zpravidla i je dostupných hned několik shellů zároveň, z nichž v jednu chvíli můžeš mít právě a pouze nastavený jako výchozí.
Shelly dostupné na Tvém stroji
Všechny shelly, které jsou na Tvém zařízení připraveny k používání, nalezneš v souboru /etc/shells. K jeho vypsání na konzoli využij příkazu cat takto:
cat /etc/shells
Rázem získáš výpis všech shellů, který může vypadat kupříkladu následovně:
/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
Výchozí shell
To, jaký shell je výchozí pro Tvůj uživatelský účet v rámci Linuxu, nalezneš v tzv. shellové proměnné. Obecně jde o proměnné, které můžeš používat, tj. číst je, měnit a vytvářet nové, kdekoliv v prostředí daného shellu.
Jméno aktuálně používaného shellu je obsahem proměnné $SHELL
. Její hodnotu zjistíš jednoduše pomocí jejího vypsání:
echo $SHELL
Výstupem může být a zpravidla i ve většině případech bude:
/bin/bash
Právě v kontextu Bashe se bavíme o nejčastěji využívaném výchozím shellu.
Zjistit aktuálně používaný Bash ale můžeš i jiným způsobem, a to pomocí:
which bash
Změna výchozího shellu
Ke změně výchozího shellu využij příkazu chsh
, který označuje zkrácené slovní spojení „change shell" s operátorem -s
. Pro nastavení /bin/bash
jakožto výchozího shellu pro aktuálně přihlášeného uživatele bude příkaz vypadat takto:
chsh -s /bin/bash
Náš první bash skript
Nutnou teorii k tomu, co je Bash zač, už známe. Pojďme se nyní proto věnovat opravdovému bash skriptování -- tedy psaní skriptů, programů, jednoduchých „aplikací", po jejichž spuštění nastane určitá akce, kterou předem definujeme ve skriptu.
Jako skript lze pro jednoduchost označit soubor, který obsahuje námi sepsané instrukce, jež má stroj, na němž skript spustíme, vykonat. Může jít nejen o sled jednoduchých příkazů, které bychom mohli také zadat přímo do příkazové řádky, ale též o různé rozhodování pomocí podmínek, tvorbu cyklů, ukládání dat do dočasných proměnných, přijímání vstupů a mnoho dalšího.
Vytvoření skriptu
Vytvořme si novou složku speciálně dedikovanou pro bash skriptování, nazvěme ji třeba jednoduše skripty
.
mkdir skripty
Přesuňme se do ní
cd skripty
A vytvořme nový soubor s názvem mujprvniskript.sh
.
touch mujprvniskript.sh
Povšimni si, že souboru jsme přiřadili koncovku .sh
. Ta označuje, že obsah bude tvořen shell skripty. V našem případě půjde o Bash.
Nyní si soubor otevři pomocí Tvého oblíbeného textového editoru, pro účely těchto skript budeme využívat jednoduchého editoru nano
.
nano mujprvniskript.sh
Pokud jej nemáš nainstalovaný, uděláš to pomocí příkazu
sudo apt-get install nano
Definování shellu k vykonání skriptu
Prázdný soubor nyní máme otevřený. O pár řádků výše jsem se zmiňoval o změně výchozího shellu pro uživatele. Jelikož každý uživatel má možnost svobodné volby, jaký shell bude používat, stejně jako my v případě volby editoru nyní, nemůžeme se vůbec spoléhat na to, že každý uživatel bude mít jako výchozí shell právě Bash.
Aby se námi napsané skripty pro shell Bash interpretovaly správně i za předpokladu, že tento shell nemá uživatel nastavený z jakéhokoliv důvodu jako výchozí, ale má jej stále nainstalovaný, musíme každý jednotlivý bash skript, tedy soubor s příponou .sh, který bude sloužit ke spuštění programu, uvodit na začátku frází značící, že následující kód se má spouštět pomocí Bashe, respektive /bin/bash
.
Uděláme to tak, že na začátek souboru na první řádek uvedeme následující:
#! /bin/bash
A je to! S čímkoliv, co teď napíšeme pod tento řádek, bude jakýkoliv stroj počítat jako se skriptem, který je napsaný v jazyce Bash (a jeho syntaxi).
Odřádkujme pomocí Enteru a přidejme jednoduchý příkaz
echo "Ahoj!"
a soubor uložme a zavřeme (v nano pomocí CTRL + O
uložíme soubor a pomocí CTRL + X
zavřeme editor).
Pro jistotu nyní uvádím, že po zavření souboru jsme se v terminálu vrátili zpět do našeho adresáře skripty, který obsahuje soubor mujprvniskript.sh
. Ověřit to můžeme pomocí příkazu:
ls
A výsledek by měl být následující
mujprvniskript.sh
Zkusme si nyní skript spustit
./mujprvniskript.sh
A ejhle! Nejde to.
-bash: ./mujprvniskript.sh: Permission denied
Nastavení správného oprávnění pro skript
Bash nám hlásí, že k požadované akci (spuštění souboru) nemáme dostatečná oprávnění. Získejme proto o souborech ve složce více informací pomocí zadání příkazu:
ls -la
A výsledek by měl být zhruba následující:
total 8
drwxr-xr-x 3 user users 96 Nov 1 20:55 .
drwxr-xr-x+ 84 user users 18 Nov 1 20:55 ..
-rw-r--r-- 1 user users 26 Nov 1 20:55 mujprvniskript.sh
Jak můžeme vidět, soubor se zde sice nachází, ale u oprávnění máme jako jeho vlastník pouze práva ke čtení (r
) a zápisu (w
). Spouštění nám není dovoleno.
Jednoduše to napravme přidáním oprávnění také pro spuštění - execution (x
):
chmod +x mujprvniskript.sh
Nyní jsme všem (vlastníkovi, skupině i ostatním) přidali oprávnění ke spuštění souboru. Možná to není žádoucí, v takovém případě můžeme využít například i oprávnění 700
, které dovolí čtení, zápis a spouštění pouze vlastníkovi a skupině a ostatním nic, pokud budeme chtít.
chmod 700 mujprvniskript.sh
Spusťme si skript nyní
./mujprvniskript.sh
Teď už se nám úspěšně vypíše naše hláška „Ahoj!
".
Komentáře
Nedílnou součástí skriptů jsou také komentáře, které zvyšují čitelnost kódů a zlepšují orientaci v něm například po delší době autorovi, ale i jiným budoucím programátorům.
Tvořit komentáře v Bashi je jednoduché. Cokoliv, co napíšete za znak hashtagu, je považováno za komentář. Ten se může nacházet jak na novém řádku, tak na konci toho existujícího.
#Toto je komentář
echo "Ahoj" #A toto je také komentář, přičemž se vypíše „Ahoj"
Práce s proměnnými
Podobně jako v jiných programovacích jazycích, i v Bashi je možné pracovat s tak elementární součástí jakýchkoliv skriptů, jako jsou proměnné. Pokud používáš jako Tvůj výchozí shell bash, můžeš si přímo v příkazové řádce vytvořit proměnnou a uložit do ní text:
POZDRAV="Ahoj, zdravím Tě!"
Potvrdíme Enterem. A obsah proměnné POZDRAV si následně můžeš v rámci stejné relace terminálu kdykoliv vypsat pomocí názvu proměnné uvozené znakem dolaru. Dej pozor na to, že při vytváření proměnné se znak dolaru neuvádí, naopak při získávání obsahu proměnné ano.
echo $POZDRAV
Úplně stejné syntaxe, za předpokladu, že využíváš Bash jako svůj výchozí shell, můžeš využít v Bash skriptech (které uvozujeme pomocí #! /bin/bash
). Otevři si tedy znovu náš soubor mujprvniskript.sh
a pod první řádek přidej
POZDRAV="Ahoj, zdravím Tě!"
echo $POZDRAV
Soubor ulož a spusť pomocí
./mujprvniskript.sh
Vypíše se text „Ahoj, zdravím Tě!
".
Uživatelský vstup
Uživatelský vstup při spuštění
Důležitým stavebním kamenem pro práci s Bash skripty je vstup od uživatele a práce s ním v rámci samotného skriptu. Může jít o používání různých operátorů, jejich hodnot a dalších parametrů, které chceme při zavolání skriptu předat, aby s nimi mohl sám dále nakládat.
Naštěstí není potřeba znovu vynalézat kolo, Bash automaticky ukládá do proměnných očíslovaných od 1 až do nekonečna všechna slova oddělená mezerou, která uvedeš za název právě spouštěného skriptu.
./mujprvniskript.sh mujArgument dalsiArgument
V rámci samotného skriptu poté s těmito argumenty můžeme pracovat pomocí proměnných $1
a $2
:
#! /bin/bash
echo $1
echo $2
Spuštění skriptu s argumenty mujArgument
a dalsiArgument
poté vypíše následující:
mujArgument
dalsiArgument
Protože nevíme, kolik argumentů nám uživatel předá, můžeme je všechny univerzálně uložit do pole pomocí:
argumenty=("$@")
A následně je vypisovat jeden po druhém. Nezapomeň, že nyní se nacházíme v poli a indexy zde začínají narozdíl od Bash proměnných od nuly!
echo ${argumenty[0]}
echo ${argumenty[1]}
Což nám vypíše totéž, co předchozí echo $1
a echo $2
.
Protože však nevíme, kolik argumentů se bude v poli argumenty nacházet, můžeme využít další z výhod Bashe, a to proměnné $@
, která obsahuje všechny argumenty pohromadě oddělené právě mezerou přesně tak, jak je uživatel zpracoval. My s nimi můžeme poté ve skriptu dále libovolně pracovat. Třeba pomocí cyklů, které si vysvětlíme později,
Uživatelský vstup při běhu
Druhou možnosti uživatelského vstupu, který může ovlivňovat chování skriptu je možnost vkládání hodnot při jeho běhu. Pro takové případy využíváme ve skriptu klíčový příkaz read
, kterým zajistíme, že se program „pozastaví" při svém běhu a umožní uživateli zadat jakoukoliv hodnotu přímo z terminálu.
Při uvádění příkazu read
je potřeba za něj přidat i název budoucí proměnné, která se tímto vstupem vytvoří a do níž se vloží data (text) od uživatele.
Vypadat to může například takto:
#! /bin/bash
echo "Jak se jmenuješ?"
read JMENO
echo "Ahoj "$JMENO", rád Tě poznávám!"
Při spuštění skriptu se poté vypíše text „Jak se jmenuješ?
", odřádkuje se a umožní se uživatelovi vložit vstup, ten vloží své jméno, potvrdí jej Enterem a program jej pozdraví. Všimni si, že i v Bashi je potřeba správně oddělovat samotný string (text) od proměnných pomocí uvozovek. Žádné spojovací výrazy zde používat nemusíme (jako například tečku nebo znak plus).
Podmínky
Podmínky jsou další nedílnou součástí bash skriptování. Uvozuje je vždy příkaz if
, který je párový, to znamená, že pro ukončení vykonávání těla podmínky použijeme příkaz fi
. Ano, skutečně jde o opačné pořadí písmen if
-> fi
.
Základní syntaxe vypadá následovně
if [ podminka ]
then
prikaz #při splnění podmínky
else
prikaz #při nesplnění podmínky
fi
Kde podmínka se uvádí do hranatých závorek a segment else
není povinný.
Operátory podmínek
Operátory pro práci se soubory
Pro práci se soubory se uvádí pouze operátor na jedné straně a na druhé straně název souboru (nic se neporovnává, pouze ověřuje):
if [ -e soubor.txt ]
then
echo "Soubor existuje"
else
echo "Soubor neexistuje"
fi
operátor | ověřuje, zda |
---|---|
-a / -e | soubor existuje |
-d | adresář existuje |
-f | soubor existuje a jedná se o standardní soubor (má platný typ) |
-h / -L | symbolický odkaz existuje |
-r | soubor existuje a máme práva ke čtení |
-w | soubor existuje a máme práva k zápisu |
-x | soubor existuje a máme práva ke spuštění |
-s | soubor existuje a jeho velikost je větší než 0 |
Operátory pro práci s textovými řetězci
Při práci s textovými řetězci lze porovnávat shodu (==) či neshodu (!=):
PRVNITEXT = "Ahoj"
DRUHYTEXT = "Ahoj"
if [ $PRVNITEXT == $DRUHYTEXT]
then
echo "Text je totožný"
else
echo "Text je rozdílný"
fi
Operátory pro práci s čísly
V případě porovnávání čísel je to o něco složitější a Bash má v tomto případě poměrně netradiční operátory. Vzhledem k tomu, že znaky jako větší než a menší než plní v Bashi již funkci pro přesměrování vstupů/výstupů (>, <, >>, <<), nelze je využít. Proto vznikly nové operátory, jejichž přehled je k dispozici níže.
operátor | zjišťuje, zda |
---|---|
-eq | se rovná (equivalent) |
-ne | se nerovná (non-equivalent) |
-qt | je větší než (greater than) |
-lt | je menší než (less than) |
-ge | je větší nebo rovno (greater/equal) |
-le | je menší nebo rovno (less/equal) |
-x | soubor existuje a máme práva ke spuštění |
-s | soubor existuje a jeho velikost je větší než 0 |
Použití operátoru je pak podobné jako u souboru s tím rozdílem, že na obou stranách nalevo i napravo od operátorů se nachází čísla, která chceme vzájemně porovnávat.
if [ 22 -gt 18 ]
then
echo "Číslo je větší"
else
echo "Číslo je menší"
fi
Cykly
Jednou z posledních podstatných dovedností při skriptování v Bashi je znalost cyklů. V Bashi můžeme standardně používat tzv. do - while
cyklus, který se iteruje (opakuje) tolikrát, dokud není splněna určitá podmínka. Zpravidla jej využíváme k procházení pole, kdy čteme prvek po prvku a s každou další iterací zvyšujeme číslo v nějaké proměnné, abychom se přesunuli k dalšímu prvku na indexu o jeden větší.
Obecná syntaxe vypadá následovně:
while [ podminka ]
do
prikaz
done
Pro úplnost si uveďme úplně základní funkční cyklus, který pětkrát po sobě vypíše pozdrav a číslo, o kolikátý pozdrav jde.
KOLIKRAT=0
while [ $KOLIKRAT -le 4 ];
do
let "KOLIKRAT++"
echo "Ahoj (už jsem Tě pozdravil "$KOLIKRAT"x)"
done
Jak můžeme vidět, zvětšování proměnné KOLIKRAT
o jednu je zde řešené pomocí
let "KOLIKRAT++"
Máme však více možností, jak toto provést. Například i takto:
let "KOLIKRAT=KOLIKRAT+1"
let "KOLIKRAT+=1"
let "KOLIKRAT++"
Nebo i úplně jinak:
KOLIKRAT=$((KOLIKRAT+1))
((KOLIKRAT=KOLIKRAT+1))
((KOLIKRAT+=1))
((KOLIKRAT++))
A co dál?
Bash toho umí mnohem víc. Mimo to, co jsme se v této kapitole naučili, lze v Bashi ke skriptování využívat jakékoliv další Linuxové příkazy, které jsou na daném stroji k dispozici. To vše lze navíc spojit i se základní znalostí práce s příkazy, větvením, pipeningem a směrováním vstupů a výstupů, čemuž jsme se věnovali ve skriptech už v předešlých kapitolách pro studenty 1. ročníku.