Skip to content
On this page

Bash Scripting

V prvním ročníku jsme si vysvětlili jak fungují základní příkazy a některé podmínky. V této kapitole se naučíme přímo psát kód, který později můžeme spustit jedním příkazem.

Skripty, shebang

Než se dostaneme k samotnému psaní bash skriptů, měli bychom se podívat na to jak náš shell pozná že soubor je bash script. Začneme tím, že vytvoříme soubor s příponou .sh, třeba online.sh, samozřejmě to, že soubor končí příponou .sh neznamená že jde o bash script, ale jde o takovou konvenci, samozřejmě můžete použít jakoukoliv příponu chcete, případně nemusíte příponu definovat vůbec. Nyní můžeme do našeho scriptu zapsat nějáké příkazy, tady je nějáký příklad:

shell
echo "Checking internet connection..."
curl -s https://vospel.cz
echo "Done"

Tento skript můžeme spustit pomocí příkazu bash online.sh. Šikovné že? Všechny 3 příkazy se spustily, a jejich výstup se vypsal do terminálu. Je ale potřeba neustále psát bash když skript spouštíme... A co když skript chci spustit v jiném shellu jako sh, fish, zsh nebo jiném, musel bych si pamatovat jaký shell používat pro jaký script. Proto tu máme takzvaný shegbang. Pokud na první řádek přidáme #!/sbin/bash, linux pozná že skript chceme spustit pomocí příkazu bash. To samé můžeme například udělat i s /bin/zsh nebo jakýmkoliv jiným shellem.

Jakmile přidáme shebang, skript můžeme spustit pomocí ./online.sh. Pokud nám shell hlásí problém s oprávněním, musíme mu přidat execute permission pomocí chmod +x ./online.sh

Podmínky (if)

Jestli si pamatujete podmíněné spouštění více příkazů, existují způsoby jak si toto ulehčit. Pokud jste někdy programovali, znáte if statement (v rustu expression), který zkontroluje podmínku v závorkách a pokud je true spustí kód v jeho bloku. ({}) Pokud je false, spustí kód v else bloku. V bashi je to podobné ale místo bloků se používají speciální statementy jako then, else nebo fi.

shell
#!/bin/bash
echo "Checking your internet connection..."

if curl -s https://vospel.cz > /dev/null 2> /dev/null; then
  echo "Success, your internet connection is working"
else
  echo "You don't have an internet connection :("
fi

Všiměte si, že nemusíme definovat závorky okolo naší podmínky, podmínka je vše, co je za if a před ;. Také si můžete všimnout jak přesměrovávám výstup do /dev/null, tedy aby načítání stránky vospel.cz nevypisovalo žádný text, který by uživatele mohl mást.

Test ([])

Ačkoliv jsem dříve zmínil, že podmínky nemusí mít závokry, pokud chceme dělat komplikovanější kontroly jako rovná se, je větší/menší atd. závorky použít musíme. Předchozí přiklad fungoval podle exit kódu curl příkazu, pokud byl ůspěšný (0) tak se spustí then, jinak else. Níže ukážu příklad porovnávání.

shell
#!/bin/bash

ONLINE="false"

if [ "$ONLINE" == "false" ]; then
  echo "You are not connected to the internet"
fi

if [ "$ONLINE" != "false" ]; then
  echo "You are Connected!"
fi

Tyto hranaté závorky jsou pouze alias pro příkaz test, který porovnává argumenty (podle operandů) a podle toho vrací exit kódy. Mimo ==, != můžeme použít i aritmetické porovnávání jako -eq, -gt, -le.

Matematické operace (expr, (()))

Pokud jste si už zkusili napsat vlastní bash script a snažili jste se sečíst dvě čísla, asi jste si všimli, že bash operátory bere jako text. Pokud chcete bash instruovat aby pracoval s čísly, můžete použít expr nebo dvojité závorky. Obojí je velmi podobné, ale například v sh se nemůžou používat dvojité závorky. Na druhou stranu u dvojitých závorek nemusíme označovat proměné dolary a nemusíme například escapovat * atd.

shell
#!/bin/bash

HODNOTA=5
# Zde použijeme expr
echo $(expr 10 \* "$HODNOTA")

# Zde použijeme dvojité závorky
echo $((10 * HODNOTA))

Všimněte si, že před závorky dáváme dolar, to shellu říká, že to co je v závorkách chceme spustit, že chceme vytvořit takzvaný subshell. Kdyby tam dolar nebyl, příkaz / příklad by se nespustil a pouze by se vypsalo (expr 10 \* "$HODNOTA").

Uživatelský vstup (argumenty, read)

Sice je hezké že si můžeme dělat různé podmínky atd, ale to je nám k ničemu, jelikož pracujeme s už předdefinovanými hodnotami. To se sice může hodit například pokud potřebujeme po každém spuštění zapnout bluetooth nebo automatizovat jiný úkol, ale pokud chceme například potvrzení od uživatele, že opravdu chce spustit velmi nebezpečný příkaz, musíme uživatelský vstup nějákým způsobem číst.

Jedním ze způsobů je příkaz read, který počká na to než uživatel napíše jakoukoliv hodnotu a dá enter. Tuto hodnotu pak zapíše do proměné REPLY

shell
#!/bin/bash

echo "This command is really dangerous, are you sure you want to execute it?"
printf "[Y/n]" # Printf je alternativa příkazu echo, dělá toho však trochu více
read

# Podmínka je case sensitive, takže pokud napíšeme malé y, příkaz se přeskočí
if [ "$REPLY" == "Y" ]; then
  echo "Installing ubuntu..."
else
  # Jako jsme si říkali v prváku každý příkaz vrací nějáký exit kód. To samé mohou udělat i bash scripty. Toto je
  # zbytečné, protože exit kód 0 se nastaví automaticky. Takto však můžeme změnit exit kód na 1 nebo jiné číslo
  exit 0
fi

Dalším způsobem jak číst uživatelský vstup je číst vstupní argumenty, vše, co napíšeme za ./skript.sh.

shell
./skript.sh --install-ubuntu -a "Another argument" Hello world

Tedy třeba --install-ubuntu, -a, Another Argument, Hello a world.

Tyto hodnoty se nám zapisujou do proměných, $0 bude samotný skript, který spoušťíme ./sript.sh, $1 bude --install-ubuntu, $2 bude -a atd. Tedy asi je jasné že se argumenty rozdělují pomocí mezery. Pokud chcete pracovat se všemi argumenty, můžete použít $@. Také můzeme vypsat počet argumentů pomocí $#.

shell
ehco "Všechny argumenty: $@"
echo "První argument: $1"

Shrnutí

  • Pokud chceme napsat list příkazů, které se mají spustit v řadě po sobě, můžeme napsat skript
  • Abychom vždy nemuseli definovat kterým shellem skript chceme spustit, musíme přidat shebang (#!/bin/bash)
  • Ve skriptech můžeme používat to samé jako když spouštíme jeden příkaz, ale můžeme to rozepsat na více řádků
  • Ve skriptech často pracujeme s různými podmínkami, matematickými expresemi a uživatelským vstupem