Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.
Poslední aktuality jsou v issue #112 (z 15. dubna).
Jednotlivá témata v tomto cvičení na sebe nenavazují. Taky tady není žádný průběžný příklad a témata tedy můžete číst a zkoušet v libovolném pořadí.
Předstartovní kontrola
- Chápete jak jsou navrženy a používány automatické (unit) testy.
Nástroje xargs
(a parallel
)
xargs
ve své nejjednodušší podobě čte standardní vstup a převádí jej na
argumenty pro uživatelem zadaný program.
Předpokládejme, že existují následující soubory:
2025-04-16.txt 2025-04-24.txt 2025-05-02.txt 2025-05-10.txt
2025-04-17.txt 2025-04-25.txt 2025-05-03.txt 2025-05-11.txt
2025-04-18.txt 2025-04-26.txt 2025-05-04.txt 2025-05-12.txt
2025-04-19.txt 2025-04-27.txt 2025-05-05.txt 2025-05-13.txt
2025-04-20.txt 2025-04-28.txt 2025-05-06.txt 2025-05-14.txt
2025-04-21.txt 2025-04-29.txt 2025-05-07.txt 2025-05-15.txt
2025-04-22.txt 2025-04-30.txt 2025-05-08.txt
2025-04-23.txt 2025-05-01.txt 2025-05-09.txt
Jako malé cvičení napište jednořádkový příkaz shellu, který tyto soubory vytvoří.
Řešení.Naším úkolem je odstranit soubory starší než 20 dní. V této verzi pouze provedeme echo příkazu, abychom nemuseli soubory při ladění našeho řešení znovu vytvářet.
cutoff_date="$( date -d "20 days ago" '+%Y%m%d' )"
for filename in 202[0-9]-[01][0-9]-[0-3][0-9].txt; do
date_num="$( basename "$filename" .txt | tr -d '-' )"
if [ "$date_num" -lt "$cutoff_date" ]; then
echo rm "$filename"
fi
done
To znamená, že program rm
by byl volán několikrát, přičemž by vždy
odstranil jen jeden soubor. Režie spojená se spouštěním nového procesu by
se mohla stát vážným úzkým hrdlem pro větší skripty (uvažujte například o
tisících souborů).
Bylo by mnohem lepší, kdybychom rm
zavolali jen jednou a zadali mu seznam
souborů, které má odstranit (tj. jako více argumentů).
Zde je řešením xargs
. Trochu program upravíme:
cutoff_date="$( date -d "20 days ago" '+%Y%m%d' )"
for filename in 202[0-9]-[01][0-9]-[0-3][0-9].txt; do
date_num="$( basename "$filename" .txt | tr -d '-' )"
if [ "$date_num" -lt "$cutoff_date" ]; then
echo "$filename"
fi
done | xargs echo rm
Místo toho, abychom soubor hned odstranili, vypíšeme pouze jeho název a
celou smyčku přesměrujeme rourou do xargs
, kde všechny jeho běžné
argumenty odkazují na program, který má být spuštěn.
Místo mnoha řádků s rm ...
uvidíme jen jeden dlouhý řádek s jediným
voláním rm
.
Další situací, kdy se xargs
může hodit, je sestavování složitého
příkazového řádku nebo situace, kdy by použití nahrazování příkazů ($( ... )
) způsobilo nečitelnost skriptu.
Záludné názvy souborů mohou samozřejmě stále způsobovat problémy, protože
xargs
předpokládá, že argumenty jsou odděleny bílými znaky. (Všimněte si,
že výše jsme byli v bezpečí, protože názvy souborů byly rozumné.) To lze
změnit pomocí --delimiter
.
Pokud do xargs
přesměrováváte výstup z vašeho programu, zvažte oddělení
položek nulovým bajtem (tj. terminátorem řetězce v jazyce C, \0
).
Vzpomeňte si, co jste měli v kurzu Arduina o řetězcích C – a jak se
ukončují. To je nejbezpečnější možnost, protože tento znak se nemůže objevit
nikde uvnitř žádného argumentu. A informujte o tom xargs
pomocí -0
nebo
--null
.
Poznamenejme, že xargs
je dostatečně chytrý na to, aby si uvědomil, kdy by
byl příkazový řádek příliš dlouhý, a automaticky jej rozdělí (podrobnosti
viz manuál).
Je také dobré mít na paměti, že xargs
může příkaz provádět paralelně
(tj. rozdělit stdin na více částí a zavolat program vícekrát s různými
částmi) prostřednictvím -P
. Pokud jsou vaše shellové skripty pomalé, ale
máte dostatek procesorového výkonu, může vám to práci docela urychlit.
parallel
Tento program lze použít k paralelnímu provádění více příkazů, čímž se urychlí jejich provádění.
parallel
se chová téměř stejně jako xargs
, ale má mnohem lepší podporu
pro souběžné provádění jednotlivých úloh (nemíchání jejich výstupů,
provádění na vzdáleném stroji atd. atd.).
Rozdíly jsou poměrně dobře popsány v dokumentaci příkazu
parallel
.
Další podrobnosti naleznete v parallel_tutorial(1)
(ano, je to manuálová
stránka) a v parallel(1)
.
Správa uložiště II
Budeme pokračovat tam, kde jsme skončili ve cvičení 11.
Pokročilé připojování disků
Připojování disků není omezeno jen na ty fyzické. V další části budeme mluvit o obrazech disků, ale jsou i jiné možnosti. Je možné připojit síťový disk (třeba NFS nebo AFS, které je používané na MFF) nebo dokonce vytvořit síťové blokové zařízení a to připojit.
Práce s obrazy disků (disk images)
Linux má zabudovanou podporu pro práci s obrazy disků. To jest, se soubory, které obsahují zrcadlový obraz skutečného disku. Ve skutečnosti jste s nimi už také pracovali, pokud jste instalovali Linux do virtuálky nebo si stáhly obraz USB disk na začátku semestru.
Linux vám umožní takovýto obraz připojit, jako by to byl fyzický disk a dokonce na něm měnit soubory. To je potřeba pro následující činnosti:
- Vývoj a ladění souborových systémů (což je spíše neobvyklá činnost)
- Vykopírování souborů z obrazů disků virtuálních strojů
- Obnova souborů z poškozených disků (neobvyklé, ale k nezaplacení)
Ve všech případech ale pro připojení obrazu disku stačí systému říct, že má
k souboru přistupovat jako k dalšímu blokovému zařízení (vzpomeňte si na
/dev/sda1
z příkladu výše).
Připojování obrazů disků
Obrazy disků lze připojit téměř stejným způsobem jako bloková zařízení,
pouze je třeba přidat k příkazu mount
volbu -o loop
.
Připomeňme, že mount
vyžaduje práva roota (sudo
), proto musíte
následující příklad provést na svém vlastním počítači, nikoli na některém ze
sdílených počítačů.
Chcete-li si to vyzkoušet, můžete si stáhnout tento FAT obraz disku a připojit jej.
sudo mkdir /mnt/photos-fat
sudo mount -o loop photos.fat.img /mnt/photos-fat
... (prace se soubory v /mnt/photos-fat)
sudo umount /mnt/photos-fat
Případně můžete spustit udisksctl loop-setup
a přidat obraz disku jako
vyměnitelnou jednotku, která se automaticky připojí na plochu:
# Pomoci udisksctl and automatickeho pripojovani v GUI
udisksctl loop-setup -f fat.img
# Tohle vytiskne asi /dev/loop0 ale cislo se muze lisit
# Ted to muzete pripojit v GUI (coz muze probehnout i automaticky)
... (prace se soubory v /run/media/$(whoami)/07C5-2DF8/)
udisksctl loop-delete -b /dev/loop0
Oprava poškozených disků
Pokud nejde disk připojit, ale je možné zkopírovat jeho obsah (obvykle by se použilo dd(1)
ale cat /dev/sdX >image.raw
může stačit), můžete se ho pokusit zachránit sami.
Zkoumání a úpravy svazků (oddílů)
Toto téma přenecháme více na pokročilý kurz. Pokud se chcete něco naučit sami, můžete začít s následujícími nástroji:
fdisk(8)
btrfs(8)
mdadm(8)
lvm(8)
Zkontrolujte si zda všemu rozumíte
Testování pomocí frameworku BATS
V této části stručně popíšeme BATS – testovací systém, který používáme pro automatické testy, které se spouštějí při každém odeslání do GitLabu.
Automatické testy jsou obecně jediným rozumným způsobem, jak zajistit, aby se váš software pomalu nekazil a nerozpadal. Dobré testy zachytí regrese, zajistí, aby se chyby neobjevovaly znovu, a často slouží jako dokumentace očekávaného chování.
Motto napište nejdříve testy se může často zdát přehnané a obtížné, ale obsahuje hodně pravdy (několik důvodů je uvedeno například v tomto článku).
BATS je systém napsaný v shellu, který se zaměřuje na shellové skripty nebo jakékoli programy s rozhraním CLI. Pokud znáte jiné testovací frameworky (e.g., Pythoní unittest, pytest nobo Nose), bude vám BATS pravděpodobně připadat velmi podobný a snadno použitelný.
Obecně platí, že každý testovací případ je jedna funkce shellu a BATS nabízí několik pomocných funkcí pro strukturování testů.
Podívejme se na příklad z domovské stránky BATS:
#!/usr/bin/env bats
@test "addition using bc" {
result="$(echo 2+2 | bc)"
[ "$result" -eq 4 ]
}
@test "addition using bc"
je definice testu. BATS ji interně přeloží do
funkce (můžete si to představit jako spuštění jednoduchého skriptu sed
u
nad vstupem a jeho odeslání do sh
) a tělo je normální kód shellu.
BATS používá set -e
k ukončení kódu, kdykoli se některý program ukončí
nenulovým výstupním kódem. Pokud se tedy [
ukončí nenulou, test selže.
Kromě tohoto není v jeho základní podobě nic dalšího potřeba. I s touto základní znalostí můžete začít používat BATS k testování svých programů CLI.
Spuštění testů je jednoduché - udělejte soubor spustitelný a spusťte jej.
Můžete si vybrat z několika výstupů a pomocí -f
můžete filtrovat, které
testy se mají spustit. Další podrobnosti najdete v bats --help
nebo
zde.
Komentovaný příklad
Napišme test pro naši funkci počítající faktoriál z cvičení 09 (funkci jste si vytvořili v jednom z příkladů).
Pro účely testování budeme předpokládat, že máme naši implementaci v souboru
factorial.sh
a naše testy vložíme do souboru test_factorial.bats
.
Začneme špatnou implementací factorial.sh
, abychom viděli, jak by měly být
testy strukturovány.
#!/bin/bash
num="$1"
echo $(( num * (num - 1 ) ))
Naše první verze testu může vypadat takto.
#!/usr/bin/env bats
@test "factorial 2" {
run ./factorial.sh 2
test "$output" = "2"
}
@test "factorial 3" {
run ./factorial.sh 3
test "$output" = "6"
}
Ke spuštění našeho programu použijeme speciální příkaz BATS run
, který
zároveň zachytí jeho stdout do proměnné s názvem $output
.
A pak jednoduše ověříme správnost.
Po spuštění příkazu se pravděpodobně vypíše něco jako toto (možná i barevně).
test_factorial.bats
✓ factorial 2
✓ factorial 3
2 tests, 0 failures
Přidáme další testovací případ:
@test "factorial 4" {
run ./factorial.sh 4
test "$output" = "20"
}
Ten neprojde, ale chybová zpráva není příliš užitečná.
test_factorial.bats
✓ factorial 2
✓ factorial 3
✗ factorial 4
(in test file test_factorial.bats, line 15)
`test "$output" = "20"' failed
3 tests, 1 failure
Důvodem je to, že BATS je velmi tenký framework, který v podstatě kontroluje pouze výstupní kódy a nic víc.
Ale můžeme to zlepšit.
#!/usr/bin/env bats
check_it() {
local num="$1"
local expected="$2"
run ./factorial.sh "$num"
test "$output" = "$expected"
}
@test "factorial 2" {
check_it 2 2
}
@test "factorial 3" {
check_it 3 6
}
@test "factorial 4" {
check_it 4 24
}
Chybové hlášení není o moc lepší, ale test je takto mnohem čitelnější.
Výše uvedenou verzi si samozřejmě spusťte i sami.
Pojďme funkci check_it
ještě trochu vylepšit.
check_it() {
local num="$1"
local expected="$2"
run ./factorial.sh "$num"
if [ "$output" != "$expected" ]; then
echo "Factorial of $num computed as $output but expecting $expected." >&2
return 1
fi
}
Spusťte test znovu:
test_factorial.bats
✓ factorial 2
✓ factorial 3
✗ factorial 4
(from function `check_it' in file test_factorial.bats, line 11,
in test file test_factorial.bats, line 24)
`check_it 4 24' failed
Factorial of 4 computed as 12 but expecting 24.
3 tests, 1 failure
To poskytuje výstup, který je dostatečně dobrý pro ladění.
Přidání dalších testovacích případů je nyní hračka. Po tomto triviálním vylepšení začne naše sada testů skutečně dávat smysl.
A pomůže nám to.
Lepší testování (assertions)
BATS nabízí rozšíření pro psaní čitelnějších testů.
Místo přímého volání test
tedy můžeme použít assert_equal
, který vytvoří
hezčí zprávu.
assert_equal "očekávaná-hodnota" "$skutecna"
Testy pro NSWI177
Naše testy obsahují rozšíření assert a několik našich vlastních. Všechny
jsou součástí repozitáře, který se
stahuje
pomocí run_tests.sh
ve vašich repozitářích.
Pokud chcete spustit jen určitý test lokálně (tj. ne na GitLabu), můžete
soubor *.bats
spustit přímo.
Ú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.
Učební výstupy a kontrola po cvičení
Tato část podává 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 …
-
používat
xargs
-
vysvětlit výhody používání automatizovaných testů funkčnosti
-
vysvětlit, co je to obraz disku
-
vysvětlit, proč nejsou zapotřebí žádné speciální nástroje pro práci s obrazy disků
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 …
-
spouštět testy založené na BATS
-
porozumět základní struktuře testů BATS
-
volitelné: použít
lsblk
ke zjištění informací o úložných zařízeních -
volitelné: vytvářet jednoduché testy BATS
-
volitelné: opravit poškozené souborové systémy pomocí programů z rodiny
fsck
-
volitelné: používat
photorec
k obnově souborů z poškozeného souborového systému