Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.
- Předstartovní kontrola
- Čtení síťové konfigurace
- Průběžný příklad
-
Modularizace skriptů a načítání konfigurace (
.
asource
) - Řídicí struktury v shellových skriptech
- Parametry skriptů a getopt
-
Příkaz
read
- Větší cvičení I
- Poznámka na okraj: jak se publikují webové stránky
- SCP a rsync
- Větší cvičení II
- Lintování zdrojového kódu pomocí nástroje ShellCheck
- Úlohy k ověření vašich znalostí
- Učební výstupy
- Seznam změn na této stránce
Na tomto cvičení si rozšíříme znalosti o skriptování v shellu. Seznámíme se s řídicími strukturami a dalšími střípky, díky nimž budou naše shellové skripty výkonnější. Naučíme se ale také, jak odhalit chyby v našich skriptech, aniž bychom je spustili.
Opět budeme průběžně používat jeden příklad, na kterém se naučíme nové konstrukce shellu, ale také se seznámíme s nástroji, které nám mohou pomoci odhalit chyby v našich programech, a podíváme se také na některé síťové nástroje.
Předstartovní kontrola
- Umíte používat shellové proměnné a tzv. command substitution.
- Pamatujete si, k čemu se používá Pandoc.
- Nahráli jste váš veřejný klíč do jednoho ze souborů
05/key.[0-9].pub
.
Čtení síťové konfigurace
Než se ponoříme do hlavního tématu, uděláme malou odbočku k jedné praktické věci, která se nám bude velmi hodit. A to jak zobrazit síťovou konfiguraci počítače z příkazového řádku.
V následujícím textu budeme předpokládat, že váš počítač je připojen k internetu (to se týká i virtualizované instalace systému Linux).
Základním příkazem pro nastavování a zobrazování konfigurace sítě je ip
.
Prozatím je pro nás asi nejužitečnější ip addr
.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s31f6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
link/ether 54:e1:ad:9f:db:36 brd ff:ff:ff:ff:ff:ff
3: wlp58s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 44:03:2c:7f:0f:76 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.105/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp58s0
valid_lft 6209sec preferred_lft 6209sec
inet6 fe80::9ba5:fc4b:96e1:f281/64 scope link noprefixroute
valid_lft forever preferred_lft forever
8: vboxnet0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0a:00:27:00:00:00 brd ff:ff:ff:ff:ff:ff
Příkaz zobrazil seznam čtyř rozhraní (lo
, enp0s31f6
, wlp58s0
a
vboxnet0
), která jsou v počítači k dispozici. Váš seznam se bude lišit,
stejně tak se mohou lišit jména rozhraní.
Název rozhraní naznačuje jeho typ.
lo
je zařízení zpětné smyčky (loopback), a bude vždy přítomno. Pomocí něj můžete testovat síťové aplikace i bez “skutečného” připojení.enp0s31f6
(často takéeth*
) je kabelový ethernet.wlp58s0
je bezdrátový adaptér (wi-fi).vboxnet0
je virtuální síťová karta, kterou VirtualBox používá při vytváření virtuální podsítě pro vaše virtuální počítače (pravděpodobně ji tam mít nebudete).
Pokud jste připojeni přes VPN, můžete vidět i rozhraní tun0
.
Stav rozhraní (spuštěno – UP – nebo ne) je na stejném řádku jako název adaptéru.
Řádek začínající link/
obsahuje MAC adresu adaptéru. Řádky s inet
udávají IP adresu přidělenou tomuto rozhraní včetně specifikace rozsahu
sítě. V tomto příkladu má lo
adresu 127.0.0.1/8
(samozřejmě),
enp0s31f6
je bez adresy (state DOWN
) a wlp58s0
má adresu
192.168.0.105/24
(tj. 192.168.0.105
se síťovou maskou 255.255.255.0
).
Vaše adresy se budou mírně lišit, ale obvykle se také zobrazí privátní adresa (za NATem), protože se pravděpodobně připojujete přes směrovač (router) k vašemu poskytovateli internetu.
Průběžný příklad
Nyní se ještě vrátíme k našemu příkladu s generováním webu. Zdrojové kódy
jsou opět v našem úložišti
příkladů,
ale jak vidíte v 08/web
, je zde nyní mnohem více stránek a soubory jsme
rozdělili do více podadresářů.
Je zde adresář content
se vstupními soubory v jazyce Markdown, adresář
static
se souborem CSS a případně dalšími soubory, které budou zkopírovány
na webový server, a je tu též adresář templates
se šablonami Pandoc pro
naše stránky.
Nyní vytvoříme slušný shellový skript, který dokáže tento web vytvořit, a také web zkopíruje na webový server, aby byl veřejně dostupný.
Uznáváme, že existují speciální nástroje určené přesně na toto. Říká se jim statické generátory stránek (nebo jen SSG - static site generators) a je jich k dispozici obrovské množství. Tato úloha ale tvoří velmi dobré hřiště, na kterém si můžeme ukázat, čeho je shell schopen :-).
Začneme triviálním generátorem, který je v podstatě kopií generátoru webu z jednoho z předchozích cvičení.
Důrazně doporučujeme, abyste fragmenty zkopírovali z našeho repozitáře do
svého (klidně použijte váš repozitář pro odevzdávání úkolů) a vytvořili
revizi (commit) pro každou verzi aplikace. Použijte git
z příkazového
řádku a používejte dobré popisy commitů.
A když si pro každou část aplikace vytvoříte na Gitlabu samostatný úkol, který později uzavřete pomocí commit zprávy, procvičíte si také dobré praktiky softwarového inženýrství.
Modularizace skriptů a načítání konfigurace (.
a source
)
Dosud byly naše skripty vždy obsaženy v jediném souboru. Není divu: všechny byly poměrně krátké. Někdy však má smysl rozdělit kód do více souborů, aby bylo možné mít více skriptů, které budou některé části kódu sdílet.
To je snadné, pokud sdílený kód tvoří samostatný spustitelný
skript. Představte si, že vytvoříme soubor s názvem msg.sh
s následujícím
obsahem:
#!/bin/bash
echo "$( date '+%Y-%m-%d %H:%M:%S |' )" "$@" >&2
V dalších skriptech pak můžeme volat skript msg.sh
a pomocí něj vypisovat
logovací zprávy.
...
./msg.sh "Starting computation..."
...
./msg.sh "Computation done."
To by mohlo fungovat dobře, ale kód je tak krátký, že by asi bylo vhodnější zapsat ho jako funkci, a asi bychom také chtěli mít více funkcí v jednom souboru. Zvláště u jednořádkových kódů je poněkud nepraktické mít pro každou “funkci” samostatný soubor.
Uděláme tedy funkci a uložíme ji do nového souboru logging.sh
.
msg() {
echo "$( date '+%Y-%m-%d %H:%M:%S |' )" "$@" >&2
}
Chceme-li takové funkce použít v jiném skriptu, musíme shellu pomocí
konstrukce source
nebo .
(ano, samostatná tečka) říci, aby do skriptu
začlenil náš soubor s funkcemi.
# Oba řádky jsou ekvivalentní, v reálu bychom použili jen jeden z nich
. logging.sh
source logging.sh
...
msg "Starting computation"
Proč by prosté volání našeho skriptu nefungovalo?Odpověď.
Existují i jiná použití příkazu source
. Viděli jsme, že jej můžeme použít
k načtení sdíleného kódu, ale velmi často se používá také k načtení
konfigurace.
Představte si, že chceme uživateli umožnit definovat adresář, kam se mají ukládat vygenerované soubory.
results=/home/intro/results/
Jistě, můžeme se pokusit z tohoto souboru získat informace pomocí cut
a
grep
, ale ve skutečnosti je mnohem jednodušší prostě načíst tento soubor
pomocí source
a pak rovnou přistupovat k $results
.
Pravdou je, že uživateli dáváme mnohem více než obyčejný konfigurační
soubor, ale nemusíme myslet na jeho konkrétní formát a pokročilí uživatelé
si do konfiguračního skriptu mohou zahrnout další kouzla shellu, zatímco
začátečníci ho mohou považovat za obyčejný soubor ve formátu
promenna=hodnota
.
Toto je vlastně také způsob, jakým se konfiguruje samotný shell. Vzpomeňte
si, jak jsme do soubrou ~/.bashrc
přidali proměnnou EDITOR
. Tento soubor
se vykoná při spuštění Bashe. Nyní už by vám měl být jasný význam
následujícího úryvku, který ~/.bashrc
často obsahuje:
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
Pokud daný soubor existuje (ke správné syntaxi se dostaneme později na tomto
cvičení), zahrneme ho pomocí source
, tj. importujeme obsah souboru na toto
místo. Takže jsme importovali globální konfiguraci Bashe, která je uložená v
adresáři /etc
.
source
se chová, jako kdyby místo řádku source
byl skutečně vložen obsah
zahrnovaného souboru. Bash nemá žádnou efektní podporu jmenných prostorů ani
nic podobného.
Soubory, které mají být zahrnuty prostřednictvím source
, obvykle
neobsahují shebang a obvykle nejsou spustitelné. Je to většinou proto, aby
se zdůraznila skutečnost, že se nejedná o samostatné spustitelné soubory,
ale spíše o “knihovny”.
Totéž platí i pro moduly Pythonu: v hlavním programu obvykle vidíte shebang
(a nastavený bit x
), zatímco skutečné moduly (které importujete pomocí
import
) jsou často bez shebangů a mají pouze oprávnění rw-
.
Pokračování průběžného příkladu
Náš příklad přepracujeme na přizpůsobitelné řešení, ve kterém náš skript přečte konfiguraci webu poskytnutou uživatelem.
V adresáři s webovou stránkou vytvoříme následující soubor ssg.rc
.
# My site configuration
site_title="My site"
build_page "index.md"
build_page "rules.md"
build_page "alpha.md"
A náš hlavní skript upravíme, aby vypadal takto.
#!/bin/bash
set -ueo pipefail
msg() {
echo "$( date '+%Y-%m-%d %H:%M:%S | SSG |' )" "$@" >&2
}
get_version() {
git rev-parse --short HEAD 2>/dev/null || echo unknown
}
build_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".md" ).html"
msg "Generating $input_file => $output_file"
pandoc \
--template templates/main.html \
--metadata site_title="$site_title" \
--metadata page_version="$( get_version )" \
"src/$input_file" >"$output_file"
}
site_title="$( whoami )'s site"
mkdir -p public
source ssg.rc
cp -R static/* public/
Co jsme to vytvořili? Náš konfigurační soubor ssg.rc
obsahuje triviální
doménově specifický jazyk (DSL - domain-specific language), který řídí
generování webových stránek. Náš hlavní skript poskytuje funkci
build_page
, která se volá z tohoto skriptu.
Uvnitř této funkce sestavíme název výstupního souboru (zkuste, co udělá
basename input.md .md
!) a spustíme Pandoc.
Ve skutečnosti se jedná o velmi jednoduchý kus kódu, ale podařilo se nám rozdělit konfiguraci a vlastní generování do samostatných souborů a vytvořit opakovaně použitelný nástroj. Porovnejte, kolik práce by to dalo v jiném jazyce. Jen si představte, kolik práce by dalo analyzovat konfigurační soubor…
Než budete pokračovat, ujistěte se, že rozumíte, jak výše uvedený kód funguje. Měli byste zvládnout odpovědět na následující otázky:
- Proč není
ssg.rc
spustitelný (a proč nemá/nemusí být)? - Proč je proměnná
$site_title
nastavená před vloženímssg.rc
? - Co by se stalo, pokud bychom vložili
ssg.rc
před volánímkdir -p public
?
Řídicí struktury v shellových skriptech
Nejprve musíme zmínit, že více příkazů může být odděleno pomocí ;
(středníku). Ačkoliv v shellových skriptech je lepší psát každý příkaz na
samostatný řádek, v interaktivním módu se často může hodit zapsat více
příkazů na jeden řádek (třeba už jen proto, že to umožňuje rychlejší pohyb v
historii pomocí šipky nahoru).
Středníky uvidíme v řídicích strukturách na různých místech v roli oddělovače.
for
cykly
For cykly v shellu vždy iterují přes sadu hodnot, které jsou předány při startu cyklu.
Formát je obecně následující:
for PROMENNA in VAL1 VAL2 VAL3; do
tělo cyklu
done
Typickým příkladem použití cyklu for
je iterování přes seznam
souborů. Tento seznam je často vygenerovaný pomocí expandovaných wildcards.
Pojďme se podívat na příklad, který spočítá počet číslic ve všech souborech
*.txt
:
for i in *.txt; do
echo -n "$i: "
tr -c -d '0-9' <"$i" | wc -c
done
Všimněte si, že ve výrazu for
je jméno proměnné i
uvedeno bez $
. Dále
můžeme vidět, že proměnná může být expandovaná i uvnitř přesměrování stdin
(nebo stdout).
Když tento výraz začneme psát přímo v interaktivním shellu, prompt se změní na jednoduché >
(tedy pravděpodobně, závisí na vašem nastavení).
Shell tím naznačuje, že na vstupu očekává zbytek cyklu.
Celý skript můžeme také dostat na jednu řádku (to se ale hodí pouze pro jednorázové skripty):
for i in *.txt; do echo -n "$i: "; tr -c -d '0-9' <"$i" | wc -c; done
Pokud chceme iterovat přes hodnoty obsahující mezery, musíme hodnoty uvést v uvozovkách. Expanze wildcardů je v tomto ohledu bezpečná a bude fungovat i když názvy souborů mezery obsahují.
for i in one "two three"; do
echo "$i";
done
if
a else
Podmínka if
je v shellu trochu složitější.
Podmínka tedy ve skutečnosti nikdy není v tradičním formátu a rovná se b,
protože if
se vždy řídí exit kódem.
Syntaxe podmínky if-then-else
obecně vypadá takto:
if prikaz_kontrolujici_podminku; then
uspech
elif jiny_prikaz_pro_vetev_else_if; then
dalsi_uspech
else
prikazy_v_else_vetvi
fi
Příkaz if
musí být ukončený pomoci fi
a větve elif
a else
jsou
volitelné.
Jednoduché podmínky lze vyhodnotit pomocí příkazu test
, který již
známe. Podívejte se na příkaz man test
a zjistěte, co všechno lze
testovat.
Pojďme se podívat, jak použít if
a test
pro kontrolu, jestli se
nacházíme v Git-ovém projektu:
if test -d .git; then
echo "Jsme v korenovem adresari Git projektu."
fi
Můžeme použít i trochu elegantnější syntaxi: [
(levá hranatá závorka) je
synonymem pro test
a chová se přesně stejně, až na to, že vyžaduje jako
svůj poslední argument ]
. Pomocí této syntaxe může náš příklad vypadat
následovně:
if [ -d .git ]; then
echo "Jsme v korenovem adresari Git projektu."
fi
I v tomto případě je ale [
pouze obyčejný příkaz, jehož exit kód určuje,
která z větví příkazu if
se provede.
/usr/bin
, zjistíte, že se
tam opravdu nachází spustitelný soubor pojmenovaný [
. Ale v Bashi (tedy v
našem
shellu) je [
implementovaný i jako vestavěný příkaz, takže je o něco
rychlejší,
než kdybychom ho spouštěli jako externí program.
Občas se můžete setkat i s následujícím kódem:
if [[ -d .git ]]; then
echo "Jsme v korenovem adresari Git projektu."
fi
Tyto dvojité závorky [[ ... ]]
jsou zase jiným konstruktem a úzce souvisí
se syntaxí $(( ... ))
pro aritmetické výrazy. Tuto podmínku vyhodnocuje
přímo Bash. Její syntaxe je o něco mocnější, ale funguje pouze v
nejnovějších verzích Bashe a pravděpodobně tedy nebude fungovat v jiných
shellech.
Budeme používat pouze tradiční variantu s [
.
Cyklus while
While cykly vypadají následovně:
while prikaz_kontrolujici_cyklus; do
prikazy_ke_spusteni
done
I v tomto případě se podmínka vyhodnotí jako pravdivá, pokud
prikaz_kontrolujici_cyklus
vrátí exit kód 0.
Následující příklad hledá první ještě nezabrané jméno pro logovací soubor. Pozor – tento kód není imunní vůči souběhům (race conditions), pokud je spuštěn paralelně. Může být tedy spuštěn vícekrát, ale nikdy ve více procesech současně.
counter=1
while [ -f "/var/log/myprog/main.$counter.log" ]; do
counter=$(( counter + 1 ))
done
logfile="/var/log/myprog/main.$counter.log"
echo "Loguji do souboru $logfile" >&2
Aby byl program odolný vůči chybám souběhu (tj. při běhu ve více instancích
paralelně), museli bychom použít příkaz mkdir
. Tento příkaz nahlásí chybu
v případě, že složka již existuje (a je dostatečně atomický na to, abychom
pomocí něho dokázali zjistit, že náš program byl opravdu úspěšný a nekrade
tedy soubor někomu jinému).
Všimněte si, že pro invertování úspěšnosti (exit-kódu) programu v
následujícím kódu používáme vykřičník !
.
counter=1
while ! mkdir "/var/log/myprog/log.$counter"; do
counter=$(( counter + 1 ))
done
logfile="/var/log/myprog/log.$counter/main.log"
echo "Loguji do souboru $logfile" >&2
break
a continue
Stejně jako v dalších jazycích je možné z cyklu vyskočit pomocí příkazu
break
. Obdobně můžete používat i příkaz continue
.
Switch (aneb case ... esac
)
Shell nabízí i konstrukci case
pro případy, ve kterých potřebujeme
rozvětvit náš program na základě hodnoty proměnné. Tato konstrukce je v
zásadě podobná konstrukci switch
v dalších jazycích, ale case
má pár
shellových specifik.
Syntaxe je následující:
case hodnota_pro_vetveni in
option1) prikazy_pro_prvni_moznost ;;
option2) prikazy_pro_druhou_moznost ;;
*) defaultni_vetev ;;
esac
Podobně jako u if
musíme příkaz uzavřít stejným klíčovým slovem zapsaným
pozpátku a u každé možnosti musíme ukončit sadu příkazů pomocí dvou
středníků ;;
.
Jednotlivé možnosti v rámci case
mohou kromě přímých hodnot obsahovat i
zástupné znaky (wildcards) a |
, aby bylo porovnávání trochu flexibilnější.
Jednoduchý příklad může vypadat takto:
case "$EDITOR" in
mcedit) echo 'Midnight Commander je nejlepší' ;;
joe) echo 'Malý, ale šikovný' ;;
emacs|vi*) echo 'Wow :-)' ;;
*) echo "To někdo opravdu používá $EDITOR?" ;;
esac
Pokračování průběžného příkladu
Vyzbrojeni znalostmi o dostupných řídicích konstrukcích můžeme náš skript pro generování stránek ještě vylepšit.
Zbavíme uživatele břemene ručního zadávání seznamu vstupních souborů a místo toho soubory vyhledáme sami. Uživatelský konfigurační soubor díku tomu bude zcela volitelný.
Náš skript změníme takto.
build_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".md" ).html"
msg "Generating $input_file => $output_file"
pandoc \
--template templates/main.html \
--metadata site_title="$site_title" \
--metadata page_version="$( get_version )" \
"$input_file" >"$output_file"
}
...
if [ -f ssg.rc ]; then
source ssg.rc
fi
for page in src/*.md; do
if ! [ -f "$page" ]; then
continue
fi
build_page "$page"
done
Upravili jsme build_page
tak, aby při spouštění pandoc
nepřidával src
k názvu souboru, a sami projdeme přes soubory Markdown v adresáři src
.
Znak !
je builtin příkaz, který obrátí význam výstupního kódu, tj. chová
se jako booleovský operátor negace.
Proč testujeme -f
uvnitř smyčky?Odpověď.
Přesměrování větších částí shellu
Celé řídící struktury (např. for
, if
nebo while
se všemi příkazy
uvnitř) se chovají jako jeden příkaz. Můžeme na ně tedy použít přesměrování
jako na celek.
Pro ilustraci můžeme zprávu transformovat na velká písmena takto.
if test -d .git; then
echo "We are in a root of a Git project"
else
echo "This is not a root of a Git project"
fi | tr 'a-z' 'A-Z'
Parametry skriptů a getopt
Připomeňme, že když skript shellu obdrží parametry, můžeme k nim přistupovat
prostřednictvím speciálních proměnných $1
, $2
, $3
. Pro přístup ke všem
parametrům existuje také proměnná $@
(připomeňme, že proměnná $@
musí
být pro správnou funkci obalena uvozovkami; vysvětlení přesahuje rámec
tohoto kurzu).
Speciální proměnná $#
obsahuje počet argumentů na příkazovém řádku a $0
označuje skutečný název skriptu.
getopt
Potřebuje-li náš skript jen jeden argument, stačí přistupovat přímo k
$1
. Potřebujeme-li ale rozpoznávat různé přepínače, bude zpracování
argumentů složitější. Shell k tomu poskytuje příkaz getopt
, který nám s
jejích zpracováním pomůže.
Nebudeme popisovat všechny detaily tohoto příkazu. Namísto toho ukážeme příklad, který si můžete upravovat podle svých potřeb.
Parsování voleb příkazového řádku není bohužel příliš standardizováno napříč
různými verzemi unixu. Zde uvedený přístup funguje dobře na jakémkoli
nejnovějším Linuxu a jeho užívání je komfortní. Není však přenositelný na
jiné podobné systémy. Existuje příkaz getopts
(ano, rozdíl je pouze v tom
s navíc na konci), který je mnohem přenositelnější, ale má mnohem
omezenější možnosti.
Hlavní argumenty řídící chování příkazu getopt
jsou -o
a -l
, pomocí
kterých specifikujeme popis přepínačů, které náš program bude přijímat.
Předpokládejme, že budeme chtít podporovat přepínač --verbose
, která říká,
aby byl náš skript výřečnější, a volbu --output
specifikující alternativní
výstupní soubor. Také budeme chtít přijímat krátké verze těchto přepínačů:
-o
a -v
. Při použití --version
budeme chtít vypsat verzi našeho
skriptu. A také nemůžeme zapomenout na --help
. Zbylé argumenty skriptu
(které nejsou přepínač) budou interpretovány jako názvy vstupních souborů.
Specifikace přepínačů pro getopt
je jednoduchá:
getopt -o "vho:" -l "verbose,version,help,output:"
Jednoznakové přepínače jsou specifikovány za -o
, dlouhé volby za -l
, a
dvojtečka :
za jménem volby značí, že bude očekávat argument.
Za to přidáme --
následované vlastními argumenty. Vyzkoušejte si to:
getopt -o "vho:" -l "verbose,version,help,output:" -- --help input1.txt --output=file.txt
# prints: --help --output 'file.txt' -- 'input1.txt'
getopt -o "vho:" -l "verbose,version,help,output:" -- --help --verbose -o out.txt input2.txt
# prints: --help --verbose -o 'out.txt' -- 'input2.txt'
...
Jak vidíte, getopt
umí zpracovat vstup a zkonvertovat parametry do
unifikované podoby, přičemž argumenty, které nejsou volbami, přesune na
konec.
Následující “magický” řádek (kterému nemusíte rozumět, abyste ho mohli
používat) přenastaví $1
, $2
, atd. tak, aby obsahovaly hodnoty po
zpracování příkazem getopt
.
eval set -- "$( getopt -o "vho:" -l "verbose,version,help,output:" -- "$@" )"
Vlastní zpracování je poté celkem přímočaré:
#!/bin/bash
set -ueo pipefail
opts_short="vho:"
opts_long="verbose,version,help,output:"
# Check for bad usage first (notice the ||)
getopt -Q -o "$opts_short" -l "$opts_long" -- "$@" || exit 1
# Actually parse them (we are here only if they are correct)
eval set -- "$( getopt -o "$opts_short" -l "$opts_long" -- "$@" )"
be_quiet=true
output_file=/dev/stdout
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
echo "Usage: $0 ..."
exit 0
;;
-o|--output)
output_file="$2"
shift
;;
-v|--verbose)
be_quiet=false
;;
--version)
echo "$0 version 1.0.0"
exit 0
;;
--)
shift
break
;;
*)
echo "Unknown option $1" >&2
exit 1
;;
esac
shift
done
$be_quiet || echo "Starting the script"
for inp in "$@"; do
$be_quiet || echo "Processing $inp into $output_file ..."
done
Některé části skriptu si zaslouží vysvětlení.
true
a false
nejsou logické hodnoty, ale lze je jako takové použít.
Vzpomeňte si, jak jsme je používali na cvičení 06 (a že existují /bin/true
and /bin/false
).
exit
okamžitě ukončí shellový skript. Jeho volitelný parametr určuje
návratovou hodnotu skriptu.
shift
je speciální příkaz, který posune proměnné $1
, $2
, … o jedno
místo. Po jeho zavolání se hodnota $3
přesune do $2
, $2
se přesune do
$1
a hodnota $1
bude zahozena. Proměnná "$@"
se voláním shift
také
upraví. Celý cyklus tedy zpracuje všechny volby, dokud nenarazí na argument
--
, který odděluje volby od ostatních argumentů. Následný cyklus for
poté iteruje jen přes zbylé argumenty. Oddělovač --
není v argumentech od
uživatele vyžadován a getopt
ho případně doplní (podívejte se do výstupu
kódu výše), tudíž smyčka for iteruje přes zbylé argumenty.
Pokračování průběžného příkladu
Upravíme náš skript tak, aby akceptoval přepínač -w
nebo --watch
, s nímž
program bude neustále sledovat změny v souborech src/*.md
, a při každé
takové změně web přegeneruje.
Do našeho obrazu Linuxu jsme zapomněli zahrnout potřebný program
inotifywait
. Spusťte následující příkaz (nejprve se vás zeptá na heslo) a
nainstalujte tento program do systému Fedora.
sudo dnf install -y inotify-tools
Nejprve skript změníme, aby používal getopt
, a poté přidáme podporu pro
--watch
.
#!/bin/bash
set -ueo pipefail
msg() {
echo "$( date '+%Y-%m-%d %H:%M:%S | SSG |' )" "$@" >&2
}
get_version() {
git rev-parse --short HEAD 2>/dev/null || echo unknown
}
build_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".md" ).html"
$LOGGER "Generating $input_file => $output_file"
pandoc \
--template templates/main.html \
--metadata site_title="$site_title" \
--metadata page_version="$( get_version )" \
"$input_file" >"$output_file"
}
generate_web() {
for page in src/*.md; do
if ! [ -f "$page" ]; then
continue
fi
build_page "$page"
done
cp -R static/* public/
}
opts_short="vwh"
opts_long="verbose,version,help,watch"
getopt -Q -o "$opts_short" -l "$opts_long" -- "$@" || exit 1
eval set -- "$( getopt -o "$opts_short" -l "$opts_long" -- "$@" )"
LOGGER=:
watch_for_changes=false
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
echo "Usage: $0 ..."
exit 0
;;
-v|--verbose)
LOGGER=msg
;;
-w|--watch)
watch_for_changes=true
;;
--)
;;
*)
echo "Unknown option $1" >&2
exit 1
;;
esac
shift
done
site_title="$( whoami )'s site"
mkdir -p public
if [ -f ssg.rc ]; then
source ssg.rc
fi
generate_web
Pro skutečnou podporu přepínače --watch
použijeme inotifywait
, což je
speciální program, který obdrží seznam souborů a ukončí se v okamžiku, kdy
je některý ze souborů změněn. Náš skript tedy ve skutečnosti nebude dělat
nic, dokud nebude soubor změněn, protože inotifywait
do té doby
“zablokuje” jeho provádění.
Do našeho skriptu přidáme následující kód, který poběží dokola, bude
sledovat změny a automaticky obnovovat web. Když je skript spuštěn s
přepínačem --watch
, musíme pro ukončení použít klávesy Ctrl-C
.
...
if [ -f ssg.rc ]; then
source ssg.rc
fi
generate_web
if $watch_for_changes; then
while true; do
$LOGGER "Waiting for file change..."
inotifywait -e modify src/* src static static/*
generate_web
done
fi
Příkaz read
Doposud naše skripty buď vůbec nepotřebovaly standardní vstup, nebo vstup zcela přenechávaly jiným programům.
Pokud však potřebujete samostatně zpracovávat řádky ze vstupu, je možné v shellu standardní vstup číst po řádcích.
Potřebujeme-li ve skriptu číst ze stdin do proměnné, můžeme použít vestavěný
příkaz read
:
read FIRST_LINE <input.txt
echo "$FIRST_LINE"
Příkaz read
typicky používáme v cyklu while
pro iterování přes celý
vstup. read
taky dokáže rozdělit řádek na části oddělené bílými znaky a
přiřadit každou z nich do samostatné proměnné.
Máme-li vstup v tomto formátu, následující cyklus spočítá průměr čísel.
/dev/sdb 1008
/dev/sdb 1676
/dev/sdc 1505
/dev/sdc 4115
/dev/sdd 999
count=0
total=0
while read device duration; do
count=$(( count + 1 ))
total=$(( total + duration ))
done
echo "Average is about $(( total / count ))."
Jak můžete uhádnout z úryvku výše, read
vrací 0, dokud je schopný načítat
hodnoty ze vstupu do proměnných. Nenulovou návratovou hodnotou je oznámeno
dosažení konce souboru.
Pro některé vstupy se read
se občas může chovat příliš chytře – například
interpretuje zpětná lomítka. Pro potlačení tohoto chování lze použít read -r
.
Další užitečné parametry jsou -t
nebo -p
: použijte read --help
pro
zobrazení jejich popisu.
Pokud chceme číst z konkrétního souboru (předpokládejme, že jeho název je
uložen v proměnné $input
), můžeme také přesměrovat vstup pro celou smyčku
a skript zapsat takto:
while read device duration; do
count=$(( count + 1 ))
total=$(( total + duration ))
done <"$input"
To je vlastně docela běžné použití vzoru while read
.
Zkontrolujte, zda rozumíte tomu, jak funguje příkaz read
Předpokládejme, že máme následující textový soubor data.txt
.
ONE
TWO
Máme také následující skript reader.sh
:
#!/bin/bash
set -ueo pipefail
read -r data_one <data.txt
read -r data_two <data.txt
read -r stdin_one
read -r stdin_two
echo "data_one=${data_one}"
echo "data_two=${data_two}"
echo "stdin_one=${stdin_one}"
echo "stdin_two=${stdin_two}"
Vyberte všechna pravdivá tvrzení o výstupu následujícího volání.
./reader.sh <data.txt
You need to have enabled JavaScript for the quiz to work.
Větší cvičení I
Představte si, že máme vstupní data s výsledky zápasů v následujícím formátu (tým, vstřelené branky, dvojtečka, branky vstřelené druhým týmem, druhý tým).
alpha 2 : 0 bravo
bravo 0 : 1 charlie
alpha 5 : 4 charlie
Napište shellový skript, který vypíše tabulku se souhrnnými výsledky.
Za vítězství přidělte 3 body, za remízu 1 bod. Váš program nemusí řešit situaci, kdy mají dva týmy stejný počet bodů.
Řešení
Začneme funkcí, která obdrží dva argumenty - počty gólů, které dala každá strana v jednom zápasu - a vypíše počet přidělených bodů.
get_points() {
local goals_mine="$1"
local goals_opponent="$2"
if [ "$goals_mine" -eq "$goals_opponent" ]; then
echo 1
elif [ "$goals_mine" -gt "$goals_opponent" ]; then
echo 3
else
echo 0
fi
}
Další funkce pak spočítá body z každého zápasu.
preprocess_scores() {
local team_one team_two
local goals_one goals_two
while read -r team_one goals_one colon goals_two team_two; do
if [ "$colon" != ":" ]; then
echo "WARNING: ignoring invalid line $team_one $goals_one $colon $goals_two $team_two" >&2
continue
fi
echo "$team_one" "$( get_points "$goals_one" "$goals_two" )"
echo "$team_two" "$( get_points "$goals_two" "$goals_one" )"
done
}
Tyto dvě funkce společně transformují vstup na následující:
alpha 3
bravo 0
bravo 0
charlie 3
alpha 3
charlie 0
Na těchto datech bychom mohli zavolat náš známý skript group_sum.py
, nebo
si posčítání můžeme napsat sami v shellu. Při implementaci v shellu budeme
počítat s tím, že data jsou již seřazena podle klíče, abychom implementaci
zjednodušili.
sum_by_sorted_keys() {
local key value
local prev_key=""
local sum=0
while read -r key value; do
if [ "$key" != "$prev_key" ]; then
if [ -n "$prev_key" ]; then
echo "$prev_key $sum"
fi
prev_key="$key"
sum=0
fi
sum=$(( sum + value ))
done
if [ -n "$prev_key" ]; then
echo "$prev_key $sum"
fi
}
Proč potřebujeme, aby data byla setříděná? Mohli bychom si je setřídit sami? Fungovala by následující úprava (pouze změna tohoto řádku)?
# replacing "while read -r key value; do"
sort | while read -r key value; do
Odpověď.
Jaká změna uvnitř této funkce by tedy fungovala? Odpověď.
Tyto funkce dohromady tvoří stavební kameny pro vyřešení celé skládačky:
preprocess_scores | sum_by_keys | sort -n -k 2 -r | column -t
Je otázkou názoru, zda by tento úkol nebyl lépe řešitelný v jiném programovacím jazyce. Vše závisí na kontextu a dalších požadavcích.
Shell obvykle vyniká v situacích, kdy potřebujeme kombinovat data z více souborů, které jsou v nějakém textovém (nejlépe řádkovém) formátu.
Výhodou shellu je jeho interaktivita. Dokonce i funkce v něm lze definovat interaktivně (tj. neukládat je nejprve do žádného souboru). Výslednou pipeline můžeme snadno sestavovat postupně a po přidání každého kroku průběžně kontrolovat výstup.
Poznámka na okraj: jak se publikují webové stránky
Nyní si uděláme malou odbočku do oblasti (historie) publikování webových stránek. Publikování webových stránek dnes obvykle znamená pronájem webového prostoru, kam můžete nahrát své soubory HTML (nebo PHP), nebo dokonce pronájem nakonfigurované instance nějaké webové aplikace, například Wordpress.
Tradičně také uživatelé často obdrželi webový prostor jako součást unixového
účtu na nějakém sdíleném počítači. Nastavení se obvykle provádělo tak, že
cokoli se objevilo ve vašem $HOME/public_html
, bylo dostupné pod stránkou
example.com/~LOGIN
.
Možná jste se s takovými stránkami také setkali, typicky na univerzitních stránkách jednotlivých profesorů.
S rozvojem virtualizace (a cloudu) bylo snazší nedávat uživatelům přístup jako skutečným uživatelům systému, ale vložit další vrstvu, kde uživatel může manipulovat pouze s určitými soubory, aniž by měl přístup k shellu.
Webové stránky na laboratorních strojích
Naše laboratorní stroje (např. u-pl*
) tuto základní funkci také nabízejí.
Pomocí SSH se připojte k jednomu z nich (připomeňte si seznam adres z
cvičení 05) a vytvořte adresář ~/WWW
.
Vytvořte jednoduchý soubor HTML v adresáři WWW
(přeskočte, pokud jste sem
již předtím nahráli nějaké soubory).
echo '<html><head><title>Hello, World!</title><body><h1>Hello, World!</h1></body></html>' >index.html
Tato stránka bude k dispozici jako http://www.ms.mff.cuni.cz/~LOGIN/.
Poznamenejme, že je třeba přidat potřebná oprávnění souborového systému AFS
pomocí příkazu fs setacl
.
fs setacl ~/WWW www read
fs setacl ~/. www l
SCP a rsync
Pro kopírování souborů mezi dvěma počítači se systémem Linux můžeme použít
příkaz scp
. Ten si uvnitř vytvoří spojení SSH a zkopíruje přes něj
soubory.
Syntaxe je velmi jednoduchá a odpovídá použití obyčejného cp
:
scp local_source_file.txt user@remote_machine:remote_destination_file.txt
scp user@remote_machine:remote_source_file.txt local_destination_file.txt
Nedostatky programu SCP
Pro ty, kterým záleží na bezpečnosti, bychom měli poznamenat, že protokol SCP má některé bezpečnostní chyby. Ty lze využít k napadení místního počítače v případě, že se připojí ke škodlivému serveru.
SCP je ve skutečnosti velmi starý protokol, na kterém je vidět jeho stáří. Mezi lepší náhrady patří SFTP (pozor, liší se od FTPS – FTP přes SSL/TLS) a Rsync.
Více informací o tomto tématu naleznete na LWN.net.
Rsync
Mnohem výkonnějším nástrojem pro kopírování souborů je rsync
. Podobně
jako scp
běží přes spojení SSH, ale musí být nainstalován na obou stranách
(to obvykle není problém)
Rsync umí kopírovat celé stromy adresářů, zpracovávat symbolické odkazy (symlinky), přístupová práva a další atributy souborů. Dokáže také rozpoznat, že některé soubory již na druhé straně existují (přesně nebo přibližně), a přenést pouze rozdíly.
Syntaxe jednoduchého kopírování je stejná jako u cp
a scp
:
rsync local_source_file.txt user@remote_machine:remote_destination_file.txt
rsync local_source_file.txt user@remote_machine:remote_destination_directory/
Větší cvičení II
Vytvořili jsme skript pro výpočet bodovací tabulky. Bylo by dobré ji vygenerovat během generování celého webu.
Rozšiřte náš průběžný příklad o následující funkce. Každý soubor *.bin
v
souboru src/
bude považován za skript, který se spustí a jeho výstup se
uloží do stejnojmenného souboru HTML.
Než se podíváte na naše řešení, vyzkoušejte si to nejprve sami.
Připomeňme, že přípona souboru není důležitá a že přípona .bin
je tak
obecná, že se pod ni může schovat jakýkoli (interpretovaný) programovací
jazyk (pokud má skript správný shebang). Ve skutečnosti bude fungovat i pro
kompilované (C, Rust a podobné) programy.
Klidně se k tomuto úkolu vraťte později: je v pohodě ho teď přeskočit a přejít na další část :-).
Řešení
Změna je poměrně jednoduchá. Pro přehlednost jsme také přejmenovali
build_page
na build_markdown_page
.
build_dynamic_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".bin" ).html"
$LOGGER "Generating $input_file => $output_file"
"$input_file" >"$output_file"
}
generate_web() {
local page
for page in src/*.md; do
if ! [ -f "$page" ]; then
continue
fi
build_markdown_page "$page"
done
local script
for script in src/*.bin; do
if ! [ -f "$script" -a -x "$script" ]; then
continue
fi
build_dynamic_page "$script"
done
cp -R static/* public/
}
A náš skript pro generování tabulek můžeme rozšířit na následující.
...
as_markdown_table() {
echo
echo '| Team | Points |'
echo '| ---- | -----: |'
while read team score; do
echo '|' "$team" '|' "$score" '|'
done
echo
}
. ssg.rc
(
echo '---'
echo 'title: Scoring table'
echo '---'
echo '# Scoring table'
preprocess_scores <scores.txt | sum_by_keys | sort -n -k 2 -r | as_markdown_table
) | pandoc \
--template templates/main.html \
--metadata site_title="$site_title" \
--metadata page_version="$( git rev-parse --short HEAD 2>/dev/null || echo unknown )"
Další vylepšení skriptu
Vypadá to dobře?
To sotva. V kódu se opakovaně objevuje fragment volání Pandoc. Náš generátor stránek není dokonalý.
Pojďme jej vylepšit.
Jako druhou verzi skriptu proveďte rozšíření, které rozliší skripty
*.md.bin
a *.html.bin
. Od skriptů s příponou .html.bin
se očekává, že
budou generovat přímo HTML, zatímco .md.bin
bude generovat Markdown, který
budeme zpracovávat sami.
Řešení
...
pandoc_as_filter() {
pandoc \
--template templates/main.html \
--metadata site_title="$site_title" \
--metadata page_version="$( get_version )" \
"$@"
}
build_markdown_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".md" ).html"
$LOGGER "Generating $input_file => $output_file"
pandoc_as_filter "$input_file" >"$output_file"
}
build_dynamic_html_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".html.bin" ).html"
$LOGGER "Generating $input_file => $output_file"
"$input_file" >"$output_file"
}
build_dynamic_markdown_page() {
local input_file="$1"
local output_file="public/$( basename "$input_file" ".md.bin" ).html"
$LOGGER "Generating $input_file => $output_file"
"$input_file" | pandoc_as_filter >"$output_file"
}
generate_web() {
local page
for page in src/*.md; do
if ! [ -f "$page" ]; then
continue
fi
build_markdown_page "$page"
done
local script
for script in src/*.md.bin; do
if ! [ -f "$script" -a -x "$script" ]; then
continue
fi
build_dynamic_markdown_page "$script"
done
for script in src/*.html.bin; do
if ! [ -f "$script" -a -x "$script" ]; then
continue
fi
build_dynamic_html_page "$script"
done
cp -R static/* public/
}
...
A náš skript pro generování tabulek table.md.bin
teď lze výrazně
zjednodušit.
...
echo '---'
echo 'title: Scoring table'
echo '---'
echo '# Scoring table'
preprocess_scores <scores.txt | sum_by_keys | sort -n -k 2 -r | as_markdown_table
Poslední vylepšení
Jako poslední cvičení rozšiřte náš skript tak, aby umožňoval práci s
opakovaně použitelnými skripty. Před spuštěním skriptů *.bin
bychom měli
rozšířit $PATH
o adresář bin/
v našem adresáři SSG.
Proč to chceme udělat? V tuto chvíli je cesta k bodovací tabulce napevno
vložená (hard-coded) uvnitř skriptu table.md.bin
a skript nelze použít pro
generování více tabulek (představte si, že bychom měli dvě oddělené skupiny
týmů). Pokud bychom ale měli tento skript v $PATH
pod jménem
score_table.sh
, mohli bychom ze souboru obsahujícího výsledky zápasů
udělat také “skript” s následujícím shebangem, a díky tomu použít
score_table.sh
pro zpracování více tabulek.
#!/usr/bin/env score_table.sh
alpha 2 : 0 bravo
bravo 0 : 1 charlie
alpha 5 : 4 charlie
Jistě, toto už hraničí se zneužitím shebangu, protože jsme ze souboru s daty udělali skript, ale mohou existovat i jiné případy použití než naše primitivní SSG, kde by takové rozšíření mělo smysl.
Zde si osvěžte paměť na env
, shebangy a $PATH
.
Řešení
Změny jsou ve skutečnosti triviální.
build_dynamic_html_page() {
...
env PATH="$PATH:$PWD/bin" "$input_file" >"$output_file"
}
build_dynamic_markdown_page() {
...
env PATH="$PATH:$PWD/bin" "$input_file" | pandoc_as_filter >"$output_file"
}
A soubor bin/score_table.sh
bude také upraven na jednom řádku.
grep -v '#' "$1" | preprocess_scores | sum_by_keys | sort -n -k 2 -r | as_markdown_table
Ze vstupu vypustíme všechny řádky obsahující #
, čímž se určitě vypustí
shebang, s tím že neočekáváme, že by název týmu mohl obsahovat znak #
(později si ukážeme regulární výrazy, které by umožnily přesnější
filtrování, ale zatím to stačí takhle).
Lintování zdrojového kódu pomocí nástroje ShellCheck
Už jste napsali poměrně hodně shellových skriptů. Je tedy čas představit vám ShellCheck.
ShellCheck je nástroj, který kontroluje skripty shellu a hledá v nich časté problémy. Tyto problémy nejsou syntaktické ani logické chyby. Problémy, na které ShellCheck upozorňuje, jsou vzory, o kterých je dobře známo, že způsobují neočekávané chování, snižují výkon nebo dokonce mohou skrývat nějaká nepříjemná překvapení.
Jedním takovým příkladem může být, když váš skript obsahuje následující kus kódu.
cat input.txt | cut -d: -f 3
Víte, co by mohlo být špatně?
Technicky je tento kód správně a sám o sobě neobsahuje chybu. Použití
příkazu cat
je ale redundantní, protože vypisuje jen jeden soubor – kód
by se dal zjednodušit do následujícího tvaru beze změny funkčnosti:
cut -d: -f 3 <input.txt
Jak vidíte, v zásadě nejde o nic škodlivého.
Může to ale znamenat, že jste chtěli spojit více souborů, ale některé vám
vypadly, nebo je cat
jen pozůstatek nějaké předchozí verze skriptu. Proto
vás ShellCheck varuje.
Jiný problém, se kterým ShellCheck může pomoci, je následující kód:
dir_name=results/
if [ -d $dri_name ]; then
echo "$dir_name already exists."
fi
Tady ShellCheck detekuje překlep, protože do proměnné dri_name
nebylo
předem nic přiřazeno.
Další nástraha čeká v následujícím kódu:
if [ -d ]; then
echo "$dir_name already exists."
fi
Tohle je samozřejmě úplně špatně. Ale co myslíte – test
(nebo [
) to
příjme a vyhodnotí jako true
. Vypadá to zvláštně, ale test
s přesně
jedním argumentem ověřuje, jestli je argument neprázdný.
Dnes už pro tohle máme test -n
, ale dříve jsme neměli a musíme zachovat
zpětnou kompatibilitu. Vizte tuto
stránku pro
podrobnosti.
Je to tedy korektní kus shellového kódu, ale nejspíš nedělá to, co jsme chtěli. Zde přichází ShellCheck, aby nám pomohl.
ShellCheck umí varovat před stovkami možných problémů, jak lze vidět na této stránce. Zvykněte si jej běžně spouštět na svých shellových skriptech.
Z naší zkušenosti ShellCheck zřídkakdy dává falešně pozitivní výsledky (false positives – případy, kdy nástroj hlásí chybu, ale kód je v pořádku), ale mnohokrát nás zachránil.
Některé z hodnocených úloh, které odevzdáte, budou kontrolovány i ShellCheckem (a můžeme penalizovat vaše řešení, pokud obsahuje ShellCheckové chyby).
Spuštění kontroly Shellcheck
Spuštění Shellchecku je opravdu snadné.
shellcheck ssg.sh
Pokud chcete zobrazit i připomínky ke stylu kódu, přidejte -o all
nebo
použijte -i
pro selektivnější kontrolu.
shellcheck -o all ssg.sh
Ostatní jazyky
Podobné nástroje existují i pro jiné jazyky.
Pylint je takový nástroj pro Python, který dokáže odhalit spoustu problémů a je také velmi dobře přizpůsobitelný.
Jako cvičení si najděte takové nástroje pro váš zvolený jazyk a začněte je pravidelně používat. Mnoho nástrojů obsahuje také rozšíření IDE pro lepší uživatelský komfort.
Co si odnést
Neodhalí se tím logické chyby (nebo aspoň ne všechny), ale určitě se odhalí tzv. code smells: místa v kódu, která často vedou k chybám, nedefinovanému chování, nebo jiným problémům.
Tohle je dvakrát tak důležité, učíte-li se nějaký nový jazyk: je tu větší šance, že jste si z programovacího jazyka něco špatně vyložili, spíše než že by byla chyba v nástroji.
Úlohy k ověření vašich znalostí
Očekáváme, že následující úlohy vyřešíte ještě před příchodem na cvičení, takže se budeme moci o vašich řešeních na cvičení pobavit.
Automatické testy zveřejníme později.
Automatické testy jsou již k dispozici.
Učební výstupy
Učební výstupy podávají zhuštěný souhrn základních konceptů a dovedností, které byste měli umět vysvětlit a/nebo použít po každém cvičení. Také obsahují absolutní minimum, které je potřebné pro pochopení navazujících cvičení (a dalších předmětů).
Znalosti konceptů
Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …
-
vysvětlit, co je to kontrola stylu a linter
-
vysvětlit, jaké problémy mohou odhalit nástroje pro kontrolu stylu
-
vysvětlit problémy se souběžností, které mohou nastat při používání dočasných souborů
-
vysvětlit, jak exit kódy umožňují řídit podmínky a cykly v shellových skriptech
-
vysvětlit pro konstrukci shellu
if true; then echo "true"; fi
, jaké příkazy se v ní provádějí a jakým způsobem se vyhodnocuje -
vysvětlit, jaká hlediska jsou důležitá při rozhodování mezi použitím shellu a Pythonu
Praktické dovednosti
Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …
-
používat bezpečně v shellových skriptech dočasné soubory
-
používat v shellových skriptech řídicí struktury (
for
,while
,if
,case
) -
použít příkaz
read
-
používat
getopt
pro parsování argumentů příkazové řádky -
používat
.
asource
k načtení funkcí z různých souborů -
používat a interpretovat výsledky ShellChecku
-
použít
scp
na kopírování souborů mezi místním a vzdáleným počítačem -
volitelné: použít
rsync
pro synchronizaci celých adresářů
Seznam změn na této stránce
- 2024-04-04: Zveřejněny automatické testy.