Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.
- Uživatelské účty
- Instalace softwaru (neboli správa balíčků)
- Služby (a démoni) (services, daemons)
- Regulární výrazy (neboli regex – regular expression)
- Testování pomocí frameworku BATS
- Úlohy před cvičením (deadline: začátek vašeho cvičení, týden 17. dubna - 21. dubna)
- Úlohy po cvičení (deadline: 14. května)
- Učební výstupy
- Seznam změn na této stránce
Na tomto cvičení probereme několik témat, která nejsou příliš rozsáhlá. Navíc spolu souvisí jen částečně, takže si můžete velké části přečíst v prakticky libovolném pořadí, které vám vyhovuje.
Podíváme se na uživatelské účty v Linuxu, zjistíme, jak se instaluje software a jak se spouštějí služby. Seznámíme se také s regulárními výrazy, které se používají k vyhledávání textových vzorů, a podíváme se, jak automaticky testovat naše programy pomocí BATS.
Uživatelské účty
Tomuto tématu jsme se již několikrát věnovali: berte ho také jako osvěžení věcí, které již znáte.
Uživatelské účty v systému Linux jsou dvou základních typů. Běžné uživatelské účty pro koncové uživatele, tj. účty, ke kterým se přihlašujete prostřednictvím SSH nebo grafického rozhraní a na kterých pracujete. Dále máme systémové účty, které existují výhradně za účelem možnosti spouštět procesy pod různými uživateli pro lepší izolaci. Pod těmito účty se obvykle vůbec nepřihlašujeme.
Vaše účty na linux.ms.mff.cuni.cz
jsou prvního typu, a pokud spustíte ps -ef --forest
, uvidíte, co mají spuštěno ostatní uživatelé. Systémové účty
jsou například chrony
nebo nginx
, které slouží ke spouštění speciálních
služeb systému.
Každý uživatelský účet má číselné id (podle kterého operační systém
identifikuje uživatele) a uživatelské jméno, které je obvykle mapováno
pomocí /etc/passwd
.
Mezi uživatelskými účty na Linuxu má jeden z nich výsadní postavení.
Tento uživatel se jmenuje root
(často též superuser nebo superuživatel),
má číselný kód 0 a má v podstatě neomezená oprávnění nad běžícím strojem.
Například, přístupová práva jsou
ve skutečnosti ignorována pro uživatele root
(tj. proces běžící pod rootem
ignoruje libovolné z rw
oprávnění a může číst/zapisovat libovolný soubor).
Chcete-li přepnout na účet superuživatele, můžete použít buď příkaz sudo
(viz níže), nebo su
. Často se spouští takto, aby se zajistilo spuštění
login shellu (mimo jiné se tím také zajistí, že $HOME
ukazuje na /root
a
ne na domovský adresář běžného uživatele):
su -
Na rozdíl od jiných systémů, Linux je navržen tak aby uživatelské programy mohly vždy běžet pod normálním (obyčejným) uživatelem a nevyžadovaly rootovská oprávnění. Dokonce, některé programy (často se to týkalo třeba IRC klientů) se pod rootem odmítaly spustit.
Účet root
je vyžadován pro změny v systému jako celku.
To zahrnuje aktualizaci systému, formátování pevného disku nebo třeba
modifikaci
systémové konfigurace.
Velmi striktní oddělené běžných (pracovních) účtů a superuživatele vychází z
faktu, že Linux byl navržen jako víceuživatelský systém.
Tato filozofie je více jak 50 let stará a sahá do doby, kdy počítač byl
sdílen mnoha uživateli a jen jeden z nich – root
– byl administrátorem.
Dnes, kdy typická notebooková instalace je právě pro jednoho uživatele, je
toto rozdělení spíš umělé, ale pořád existuje.
Pravdou je, že dnešní uživatel je více ohrožen napadenou webovou stránku než neoprávněnou aktualizací systému a účet superuživatele byl navržen spíše s ohledem na to druhé. Nicméně, idea oddělených účtů dává stále smysl a obezřetný uživatel může používat různé účty pro různé aktivity (např. prohlížení sociálních sítí a práce s on-line bankovnictvím).
Správa uživatelských účtů
Považujeme ji za pokročilé téma a v tomto kurzu se omezíme na to, že vás
odkážeme na dokumentaci příkazů useradd
, userdel
a usermod
, které
vytvářejí, odstraňují a upravují uživatelské účty.
Připomeňme, že pro získání informací o existujících účtech existuje také
funkce getent
.
Měli byste také znát passwd
, který lze použít ke změně uživatelského
hesla.
sudo
Některé programy vyžadují zvýšení svých oprávnění (privilege escalation), tj. běžet s většími pravomocemi než ostatní programy. Některé to vyžadují svojí podstatou a už jsme zmiňovali set-uid bity na spustitelných souborech, které jsou použity u aplikací, které vždy vyžadují vyšší práva (bez ohledu na to, kdo je jak spustil).
Některé příkazy však vyžadují vyšší oprávnění jen občas, takže jejich spouštění jako set-uid zbytečně rozšiřuje možné vektory útoku.
Pro tyto situace je jedním z možných řešení sudo
(domovská
stránka).
Jak jméno napovídá, spustí jeden příkaz (superuser do) s oprávněními
superuživatele.
Výhodou sudo je, že administrátor může určit který příkaz poběží s vyšším
oprávněním.
Takže nedává vybraným uživatelům kompletní kontrolu nad počítačem, ale pouze
jim umožňuje
pracovat s vybranou množinou příkazů.
Například je možné dát uživateli možnost restartovat určitou službu (např. chceme testerovi umožnit restartovat webový server), aniž bychom mu dali kontrolu nad celým počítačem.
Všimněte si, že granularita sudo
je na úrovni programů.
Neomezuje, jak se program chová uvnitř.
Například, je možné omezit, že uživatelka alice
může spustit
nebezpecny_prikaz
pouze s přepínačem --neskodny-prepinac
.
Ale, pokud nebezpecny_prikaz
také čte konfiguraci ze souboru
~/.nebezpecnyrc
,
alice
může skrz něj přidat volbu --nebezpecna-volba
.
A sudo
tomu nemá jak zabránit.
Jinými slovy: jakmile jsou počáteční kontroly (před spuštěním) dokončeny,
program se chová jako by ho spustit uživatel root
.
Tohle je velmi důležité pro sdílené stroje, kde administrátor chce typicky
omezit ostatní uživatele jak nejvíce to jde.
Na druhou stranu, na desktopových (notebookových) instalacích obvyklé
nastavení je, že první vytvořený uživatel
(typicky vytvořený ještě během instalace) může spustit libovolný program se
sudo
.
Zdůvodnění je takové, že existuje stejně pouze jeden (fyzický) uživatel,
který stejně rootovské heslo zná.
To je také důvod, proč většina návodů na webu obvykle uvádí příkazy pro
správu systému včetně sudo
na
začátku každého příkazu.
Měli byste ale vždy vědět, proč sudo
pouštíte.
Nechyťte se do pasti zvyku, že když to nefunguje, zkusím to pustit se
sudo
.
Mimochodem, možností jak získat rootovský shell je
více (např. sudo bash
).
Jako bezpečný příklad můžete zkusit spustit fdisk -l
a vypsat oddíly v
systému. Při spuštění bez práv roota pravděpodobně selže s několika
hlášeními o odepřeném přístupu. Spuštění se sudo
by mělo fungovat.
Všimněte si, že zadáváte své heslo, nikoli heslo superuživatele (ostatně
pokud by to bylo heslo superuživatele, nepotřebovali byste sudo
, protože
byste mohli spustit su -
a získat root shell.
sudo fdisk -l
sudo
není jediným bezpečnostním mechanismem.
Ostatními se nebudeme do detailů zabývat, ale uvedeme alespoň odkazy na
SELinux nebo
AppArmor.
Shrnutí mechanismů je k dispozici též na
Wikipedii.
Přehled uživatelských účtů: zkontrolujte, zda rozumíte základům
Instalace softwaru (neboli správa balíčků)
Software v Linuxu je obvykle instalován prostřednictvím správce balíčků (package manager). Správce balíčků je speciální program, který se stará o instalaci, upgrade i odstranění balíčků. Balíček může být cokoliv, co lze nainstalovat; to zahrnuje:
- programy (například, balíček
ranger
nainstaluje programranger
), - datové soubory a nastavení (např.
libreoffice-langpack-cs
pro podporu češtiny v LibreOffice), - knihovna (třeba
gmp
nebogmp-devel
poskytují GNU knihovnu pro libovolně přesné výpočty), - nebo tzv. meta balíček (např.
xfce
zahrnujexfce4-terminal
,xfwm4-themes
atd.).
V tomto směru je Linux velmi podobný tomu, co znáte ze správy softwaru pomocí obchodu na vašich chytrých telefonech. Je velmi neobvyklé instalovat software na Linuxu prostřednictvím grafického instalátoru.
Výhodou centrální správy balíčků je možnost upgradovat celý systém, aniž by bylo nutné kontrolovat samostatně jednotlivé aplikace.
Je pochopitelně možné instalovat software i ručně. Z pohledu souborového systému je to jedno – správce balíčků v podstatě jen kopíruje soubory do správných adresářů. Ale ručně nainstalovaný software musí být i ručně aktualizován a věci spíš komplikuje. Takže se tomu pokud možno vyhýbejte.
Typický správce balíčků pracuje s několika repozitáři softwaru (software repository). Můžete o nich uvažovat jako by váš telefon měl několik obchodních center, kde si aplikace vybíráte. Obvykle najdete následující typy repozitářů. Je na uživateli (správci), které repozitáře bude využívat.
- Stable a testing, kde ten druhý nabízí novější verze softwaru ale s drobnou možností výskytu chyb (obvykle existuje ještě třetí repozitář, obvykle nazvaný unstable, který je pro poslední, často vývojové, verze).
- Free a non-free kde ten první obsahuje jen software bez právních překvapení. Non-free software může být zatížen patenty nebo vlastnickými právy (obvykle podle práva USA) nebo licencemi, které omezují jeho šíření.
Ani oficiální ani neoficiální repozitáře neposkytují žádnou záruku v právním slova smyslu. Nicméně, používání oficiálních (výchozích) repozitářů se považuje za bezpečné, množství útoků na tyto repozitáře je nízké a – na rozdíl od mnoha komerčních institucí – správci těchto repozitářů jsou velmi otevření v informování o podobných incidentech. Je mnohem snazší narazit na podvodnou aplikaci v “obchodu” pro váš chytrý telefon než na ní narazit v oficiálních repozitářích vaší distribuce.
dnf
(tj. správce balíčků pro Fedoru)
Fedora dříve používala jako správce balíčků yum
a lze jej nalézt v mnoha
návodech na internetu (dokonce i v poměrně nedávných). Nyní je považován za
zastaralý a raději se mu vyhněte.
Pokud jste zvyklí na yum
ze starších verzí Fedory nebo z jiných distribucí
založených na RPM, zjistíte, že dnf
je velmi podobný a v mnoha situacích
rychlejší než yum
.
Správce balíčků pro Fedoru se nazývá DNF.
Pokud jste se rozhodli používat jinou distribuci, budete muset příkazy upravit tak, aby odpovídaly vašemu systému. Obecně by operace měly být dost podobné, ale nemůžeme zde poskytnout návod pro každého správce balíčků.
Pomocí příkazu search
můžete získat seznam balíčků, které odpovídají
zadanému názvu. Všimněte si, že vyhledávání není privilegovanou operací, a
proto nevyžaduje sudo
.
dnf search arduino
dnf search atool
Všimněte si, že vyhledávání velmi obecného výrazu může přinést stovky výsledků.
Výstup je v následujícím formátu:
atool.noarch : A perl script for managing file archives of various types
ratools.x86_64
Řetězce .noarch
a .x86_64
popisují povahu balíčku. noarch
obvykle
označuje datový balíček nebo balíček používající interpretované jazyky,
zatímco .x86_64
označuje balíček s binárními soubory pro architekturu
x86-64 (např. napsaný v jazyce C nebo Rust a následně zkompilovaný do
strojového kódu).
Chcete-li nainstalovat softwarový balíček, spusťte dnf
s příkazem
install
a zadejte mu název balíčku, který chcete nainstalovat. Zde už je
nutné sudo
, protože upravujeme systém.
sudo dnf install atool
Některé aplikace nejsou součástí žádného softwarého repozitáře, ale přesto je můžete stáhnout ve formátu srozumitelném správci balíčků. To je lepší situace než instalovat soubory ručně, protože váš správce balíčků o nich ví (i když je nemůže automaticky aktualizovat). Jedním z takových příkladů je klient Zoom, který se musí nainstalovat takto:
sudo dnf install "https://zoom.us/client/latest/zoom_x86_64.rpm"
Chcete-li aktualizovat celý systém, jednoduše spusťte sudo dnf upgrade
.
DNF si vyžádá potvrzení a poté provede aktualizaci všech dostupných balíčků.
Všimněte si, že na rozdíl od jiných systémů si můžete vždy vybrat, kdy chcete provést upgrade. Systém za vás nikdy nerestartuje počítač ani nezobrazí zprávu o nutném restartu, pokud o to výslovně nepožádáte.
Pokud chcete nainstalovat celou skupinu balíčků, můžete použít dnf grouplist
pro zobrazení jejich seznamu a sudo dnf install @GROUP_NAME
pro
jejich instalaci.
Výše uvedené příkazy obsahují základní informace o správě balíčků v systému Fedora. Následující odkazy poskytují další informace. Pokud již systém trochu znáte, je dobrým zdrojem informací oficiální stránka Wiki.
Pro začátečníky jsou pravděpodobně lepším výchozím bodem tento průvodce DNF a tento návod.
Správa balíčků: zkontrolujte si, zda této části rozumíte
Služby (a démoni) (services, daemons)
V kontextu operačních systémů, termínem služba (service) odkazujeme na
libovolný program, který běží na pozadí (takže žádné GUI, stdin z
/dev/null
) a poskytuje nějakou službu dalším programům.
Typickým příkladem může být služba tisku, která se stará o nalezení tiskáren a koncovým aplikacím poskytuje jejich seznam (tj. uživatelské aplikace nemusí tiskárny samy vyhledávat). Jiný příklad je webový server: poskytuje soubory přes HTTP protokol webovým prohlížečům.
Ve světě Unixových systémů jsou tyto programy často označovány jako démoni
(což asi naráží na řeckou mytologii, kde démon bylo stvoření pracující v
pozadí); tradičně názvy těchto programů končí písmenem d
.
Například, populární webový server Apache je ve skutečnosti spouštěn jako
program httpd
a SSH server běží jako sshd
.
Démoni fungují jinak než normální programy.
Když jsou spuštěny, přečtou si konfiguraci (typicky ze souborů pod /etc/
),
spustí se a čekají na požadavky (představte si webový server poslouchající
na portu 80).
Změna jejich chování je obvykle provedena změnou konfiguračního souboru a
jejich restartováním.
Protože běží na pozadí, nemají přístup k interaktivnímu stdinu a restart
(nebo ukončení) se provádí pomocí signálů.
Připomeňme, že jsme již dříve viděli nástroj kill
pro zastavení programů.
Tato utilita je ve skutečnosti univerzálnější, protože může také vyslat
signál, který může program zachytit a může na něj reagovat (podrobnosti
uvidíme později). Takovým příkladem je reakce na speciální signál, který
dává programu pokyn, aby znovu načetl svou konfiguraci.
Protože požadavek na restart démona je poměrně běžný (a posílání signálů je
těžkopádné kvůli nutnosti zjišťovat PID), existují i speciální programy,
které si PID najdou a pošlou ten správný signál.
Obvykle se nazývají řídící skripty a pro některé démony najdete soubory
serviced
(vlastní démon) a servicectl
pro jeho řízení.
Sjednocené řízení démonů
Výše uvedené principy jsou v podstatě stejné pro všechny služby, proto
obvykle existuje sada skriptů, která toto chování sjednocuje.
Takže místo toho, abyste museli volat daemonctl
, distribuce vám nabídne
speciální příkaz, kterým můžete ovládat libovolnou službu.
Obvykle tedy použijete něco jako toto:
service [start|stop|restart] jmeno-daemona
V současnosti je nejobvyklejším nástrojem pro tuto úlohu systemd.
O logování
Většina služeb poskytuje tzv. logy (protokoly). Obsahují záznam o každé důležité akci, kterou démon provedl.
Například, webový server typicky zaznamená, které stránky obsloužil společně s informací o klientovi.
Obvykle pro každou služba určíte, jak má být logování detailní. Ladění problému v konfiguraci vyžaduje detailní zprávy, v produkčním prostředí naopak zapnete jen logování vážných chyb pro zvýšení výkonu.
Systemd
Systemd je jedním z nejpoužívanějších nástrojů pro správu systémových služeb v dnešním světě Linuxu.
Nebudeme zabíhat do podrobností a pouze si projdeme dva nejdůležitější
příkazy: systemctl
a journalctl
.
Všimněte si, že systemd
je démon, zatímco systemctl
je příkaz pro jeho
ovládání.
Spuštění a zastavení služby
Spuštění služby pomocí systemd je velmi jednoduché. Následující příkaz
spustí sshd
, server SSH:
sudo systemctl start sshd
Pokud služba již byla spuštěna, nic se nestane.
Zkontrolujte, zda se nyní můžete připojit ke svému počítači pomocí následujícího příkazu:
ssh your-login@localhost
Ke zjištění stavu služby se používá příkaz status
(ten lze spustit i bez
sudo
, ale může zobrazit méně informací):
sudo systemctl status sshd
● sshd.service - OpenSSH Daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2021-03-01 14:31:40 CET; 2 months 3 days ago
Main PID: 560 (sshd)
Tasks: 1 (limit: 9230)
Memory: 900.0K
CPU: 16ms
CGroup: /system.slice/sshd.service
└─560 sshd: /usr/bin/sshd -D [listener] 0 of 10-100 startups
Warning: journal has been rotated since unit was started, output may be incomplete.
Vidíme, že služba je spuštěna, většina položek je jasná. Soubor
/usr/lib/systemd/system/sshd.service
obsahuje samotnou konfiguraci služby
(např. jak službu spustit/zastavit/restartovat), nikoli vlastní konfiguraci
démona SSH, která je uvnitř /etc/ssh
.
Pokud démona SSH na notebooku nebudete používat, je bezpečnější ho zastavit:
sudo systemctl stop sshd
Povolení a zakázání služby
Pokud chcete službu spouštět při každém spuštění systému, můžete ji povolit:
sudo systemctl enable sshd
Systemd se postará o správné pořadí jednotlivých služeb (takže SSH server se spustí až po inicializaci sítě atp.).
Pokud si již nepřejete, aby byl démon SSH automaticky spuštěn, zavolejte
příkaz s disable
.
Všimněte si, že jak enable
, tak disable
nemění aktuální stav služby:
pokud nechcete čekat na restart, musíte stále použít start
/stop
. (Pro
větší pohodlí je k dispozici příkaz systemctl enable --now sshd
, který
službu také spustí.)
Logy
Většina systémových služeb uchovává logy o své práci. Ty jsou obvykle
uloženy v adresáři /var/log/
. Některé služby si je vytvářejí samy a jde o
jednoduché textové soubory, jejichž formát je specifický pro jednotlivé
služby a jejich konfiguraci.
Mnoho služeb ale používá centrální logovací službu, která uchovává všechny své logy v jednotném formátu a kterou lze nakonfigurovat pro jejich třídění, odesílání po síti, odstraňování starých záznamů atp.
Ve Fedoře se logovací služba nazývá journald
. Ukládá protokoly do
kryptograficky podepsaných binárních souborů, které nejsou přímo
čitelné. Lze je číst pomocí příkazu journalctl
.
Následující příkaz například zobrazí logy démona SSH:
journalctl -u sshd
Více …
Pokud vás toto téma zajímá, přečtěte si příslušné manuálové stránky. Těchto několik odstavců berte jako velmi stručný úvod do tématu, který vám umožní základní správu systému.
Regulární výrazy (neboli regex – regular expression)
Už jsme zmínili, že systémy Unixové rodiny jsou založeny na textových souborech. Utility, které jsme zatím viděli, nám umožnili základní operace s nimi, ale žádné z nich nebyly opravdu mocné. Použitím regulárních výrazů to změníme.
Nebudeme zde řešit teoretické detaily – na to je předmět Automaty a gramatiky. My se na regulární výrazy budeme dívat jako na jednoduché nástroje pro hledání vzorů (patternů) v textu.
Například nás můžou zajímat:
- řádky začínající datem a obsahující HTTP kód 404,
- řádky obsahující náš login,
- nebo řádky předcházející řádkům s platnými názvy souborů.
grep
a
sed
. Knihovny pro práci s regulárními výrazy jsou taky k dispozici ve
většině programovacích jazyků, ale opět pozor na rozdíly v jejich syntaxi.
Nejzákladnějším nástrojem na vyhledávání regulárními výrazy v souborech je
grep
. (Legenda říká, že g
ve jméně znamená “globally”, re
je regex a
p
print.) Spustíme-li grep
regex soubor, vypíší se všechny řádky
souboru, které odpovídají zadanému regulárnímu výrazu. Během cvičení si
vyzkoušíme spoustu příkladů.
Syntaxe regexů
V tom nejjednodušším tvaru regex hledá zadaný řetězec (obvykle s ohledem na velikost písmen).
system
Tímto zachytíme všechny podřetězce system
v textu. Při použití grepu to
znamená, že všechny řádky obsahující system
(kdekoliv) se vypíší.
Chceme-li hledat řádky začínající tímto slovem, musíme přidat ukotvení ^
.
^system
Má-li řádek končit zadaným vzorem, potřebujeme použít ukotvení $
. V
shellu je bezpečnější kolem vzoru používat jednoduché uvozovky, abychom se
vyvarovali expanzi proměnných, atp.
system$
Také můžeme najít všechny řádky začínající jedním z písmen r
, s
, nebo
t
pomocí seznamu [...]
.
^[rst]
Vypadá to trochu jako wildcard, ale regexy jsou mnohem silnější a syntaxe se trochu liší.
Zkusme najít všechna třímístná čísla:
[0-9][0-9][0-9]
Tomuto budou vyhovovat jak všechna třímístná čísla, tak třeba i čtyřmístná: regulární výrazy bez ukotvení se vůbec nestarají o okolní znaky.
Můžeme také hledat řádky nezačínající žádným ze znaků mezi r
a
z
. (První ^
je ukotvení, zatímco druhá neguje množinu v []
.)
^[^r-z]
Kvantifikátor *
značí, že předchozí část regexu se může vyskytovat
vícekrát nebo třeba vůbec. Např. tímto najdeme všechny řádky, které obsahují
jen číslice:
^[0-9]*$
Poznamenejme, že to nevyžaduje, aby všechny číslice byly stejné.
Tečka .
odpovídá libovolnému jednomu znaku (kromě zalomení
řádku). Následující regex pak zachytí řádky začínající super
a končící
ious
:
^super.*ious$
Abychom mohli aplikovat *
na složitější podvýraz, můžeme jej obalit
(...)
. Následující regex zachytí bana
, banana
, bananana
, atd.:
ba(na)*na
Použijeme-li +
místo *
, je vyžadován aspoň jeden výskyt. Tímto tak
zachytíme všechna dekadická čísla:
[0-9]+
Svislá čára (|
, příp. pipa) může oddělovat alternativy. Např. můžeme
zachytit řádky složené z Meow
a Quork
:
^(Meow|Quork)*$
Konstrukce [abc]
je tak jen zkratkou pro (a|b|c)
.
Další užitečnou zkratkou je kvantifikátor {
N}
: ten říká, že předchozí
regex se musí opakovat N krát. Můžeme také použít {
N,
M}
pro
rozsah. Např. můžeme zachytit řádky obsahující 4–10 malých písmen
uzavřených v uvozovkách:
^"[a-z]{4,10}"$
Nakonec je tu zpětné lomítko, které mění to, jestli je následující znak
považovaný za speciální. \.
zachytí opravdu tečku a \*
hvězdičku. Naproti tomu mnoho dialektů regexů (vč. grepu bez dalších
přepínačů) vyžaduje +
, (
, |
a {
odescapované, aby byly rozpoznány
jako regexové operátory. (Můžete spustit grep -E
nebo egrep
pro
aktivování rozšířených regulárních výrazů, které všechny speciální znaky
rozpoznávají jako operátory bez zpětných lomítek.)
grep
skončí s nulovým exit kódem pouze pokud našel alespoň jeden
odpovídající řádek.
Takže může být využit takto:
if ! echo "$input" | grep 'regex'; then
echo "Input is not in correct format." >&2
...
fi
Nahrazování v textu
Plná síla regulárních výrazů se ukáže, když je použijeme na nahrazování
vzorů. Ukážeme si to na příkazu sed
(stream editor), který umí provádět
textové transformace založené na regulárních výrazech.
sed
a grep
používají mírně odlišnou syntaxi regexu. Pokud si nejste
jisti, vždy se podívejte do manuálové stránky. Největší rozdíly mezi
nástroji a jazyky jsou obecně v zacházení se speciálními znaky pro opakování
nebo seskupování (()
, {}
).
V nejjednodušší podobě, sed
nahrazuje jedno slovo jiným. Příkaz přečte
nahrazení (s
, substitute), pak jednoznakový oddělovač následovaný textem
k nahrazení (levá strana substituce), opět stejný oddělovač, pak náhrada
(pravá strana) a nakonec opět oddělovač. (Oddělovač je obvykle :
, /
,
nebo #
, ale obecně to může být libovolný znak, který se nepoužívá ve
zbytku příkazu bez escapování.)
sed 's:magna:angam:' lorem.txt
Poznamenejme, že tímto nahradíme vždy jen první výskyt na každém
řádku. Přidání modifikátoru g
(global) na konec příkazu způsobí nahrazení
všech výskytů:
sed 's:magna:angam:g' lorem.txt
Text, který má být nahrazen, může být libovolný regulární výraz, např.:
sed 's:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]:DATE-REDACTED-OUT:g' lorem.txt
Pravá strana se může odkazovat na text zachycený levou stranou. Můžeme
použít &
pro celou levou stranu nebo \
n pro n-tou skupinu (...)
na
levé straně.
Následující příklad převede datum do českého tvaru. Podobně jako u grep
u
musíme odescapovat znaky (
a )
, aby fungovaly jako seskupující operátory
namísto literálů (
a )
.
sed 's:\([0-9][0-9][0-9][0-9]\)-\([0-9][0-9]\)-\([0-9][0-9]\):\3. \2. \1:g'
Cvičení
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í 08.
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 před cvičením (deadline: začátek vašeho cvičení, týden 17. dubna - 21. dubna)
Následující úlohy musí být vyřešeny a odevzdány před příchodem na vaše cvičení. Pokud máte cvičení ve středu v 10.40, soubory musí být nahrány do vašeho projektu (repozitáře) na GitLabu nejpozději ve středu v 10.39.
Pro virtuální cvičení je deadline úterý 9:00 (každý týden, vždy ráno, bez ohledu na možné státní svátky apod.).
Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).
10/uid.txt
(20 bodů, skupina admin
)
Do tohoto souboru vložte (číselné) uživatelské ID svého účtu na
linux.ms.mff.cuni.cz
.
10/fdisk.txt
(30 bodů, skupina admin
)
Uložte do souboru seznam diskových oddílů počítače, jak je vypsal fdisk -l
.
Ujistěte se, že jste uložili celý výstup a nic nekopírovali ručně (tj. použijte přesměrování).
root
.
10/netcfg.sh
(20 bodů, skupina net
)
Napište filtr
pro výstup příkazu ip addr
, který vypíše název zařízení následovaný jeho
adresou IPv4 a délkou síťového prefixu.
Pro rozhraní, které nemá přidělenou adresu IPv4, vypište místo ní speciální
adresu 0.0.0.0/0
.
Příklad z cvičení 08 by byl zpracován do následujícího výstupu:
lo 127.0.0.1/8
enp0s31f6 0.0.0.0/0
wlp58s0 192.168.0.105/24
vboxnet0 0.0.0.0/0
Případ chybějící IP adresy váš skript pravděpodobně značně
zkomplikuje. Poznamenejme, že tento případ je ohodnocen pouze 5 body,
protože těžiště úlohy spočívá v sestavení správného regulárního výrazu v
sed
u, který odpovídá příslušným řádkům (použití sed
u není povinné, ale
pro tuto úlohu se doporučuje).
10/normalize.sh
(30 bodů, skupina shell
)
Napište skript, který znormalizuje danou cestu.
Skript očekává jediný argument: cestu, kterou má normalizovat. Můžete předpokládat, že argument bude vždy uveden.
Skript provede normalizaci cesty následujícím způsobem:
- odkazy na aktuální adresář budou odstraněny, protože jsou nadbytečné
- odkazy na nadřazený (rodičovský) adresář budou odstraněny takovým způsobem, aby se nezměnil význam cesty (možná i opakovaně)
- skript nebude převádět relativní cestu na absolutní nebo naopak
- skript nebude kontrolovat, zda-li soubor skutečně existuje
Následující příklady ilustrují očekávané chování.
/etc/passwd
⇒/etc/passwd
a/b/././c/d
⇒a/b/c/d
/a/b/../c
⇒/a/c
/usr/../etc/
⇒/etc/
Můžete předpokládat, že jednotlivé komponenty cesty neobsahují znak nového
řádku nebo další speciální znaky jako :
, "
, '
nebo nějakou escape
sekvenci.
Nápověda: sed ':x; s/abb/ba/; tx'
zajistí, že s/abb/ba/
je voláno
opakovaně, dokud probíhá náhrada (:x
definuje návěští a tx
je podmíněný
skok na návěští pokud předchozí nahrazování změnilo vstup). Vyzkoušejte s
echo 'abbbb' | sed ...
.
Smyslem cvičení je prověřit vaše znalosti regexů, nikoli používání
realpath
nebo něčeho podobného.
Úlohy po cvičení (deadline: 14. května)
Očekáváme, že následující úlohy vyřešíte po cvičení, tj. poté, co budete mít zpětnou vazbu k vašim řešením úloh před cvičením.
Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).
10/factorial.bats
(40 bodů, skupina devel
)
Poznámka: popis úlohy byl přepsán, aby lépe zachytil její účel. Také jsme
aktualizovali výchozí implementaci, aby používala rozumné chybové hlášky a
také kontroluje proměnnou $status
, do které BATSová funkce run
uloží
exit kód spuštěného programu.
Účelem úlohy je vyzkoušet si psaní testů. Napíšete testy pro implementaci faktoriálu v shellu.
Faktoriál bude počítán funkcí factorial
, které bere jeden argument a
vytiskne spočítaný výsledek. Jako dobře vychovaná shellová funkce skončí při
chybě s nenulovou návratovou hodnotou.
Vaše testy by měly být napsána proti specifikaci uvedené výše.
Naše testy pak do implementaci vloží různé chyby a očekáváme, že vaše testy tyto problémy odhalí. Nicméně, vy poskytnete jen jednu sadu testů, kterou my spustíme s různými implementacemi.
Vaše testy musí načíst (ve smyslu příkazu source
) factorial.sh
z
aktuálního adresáře, který bude obsahovat vlastní implementaci funkce
factorial
v shellu.
Takže factorial.sh
může pro začátek obsahovat následující (vaše testy by
měly odhalit hodně problémů v tomto kusu kódu).
factorial() {
local n="$1"
echo $(( n * ( n - 1 ) ))
}
Následující kód je dobrým začátkem implementace tohoto úkolu.
#!/usr/bin/env bats
source "factorial.sh"
check_it() {
local num="$1"
local expected="$2"
run factorial "$num"
if [ "$status" -ne 0 ]; then
echo "Function not terminated with zero exit code." >&2
false
fi
if [ "$output" != "$expected" ]; then
echo "Wrong output for $num: got '$output', expecting '$expected'." >&2
false
fi
}
@test "factorial 2" {
check_it 2 2
}
@test "factorial 3" {
check_it 3 6
}
@test "factorial 4" {
check_it 4 24
}
Naše testy nebudou kontrolovat přesná chybová hlášení, ale budou kontrolovat, zda některé testy sady selhávají (pomocí návratového kódu BATS).
Podívejte se na implementaci testů pro snazší pochopení toho, co po vás chceme.
Netestujte pro čísla větší než 10.
Stojí za zmínku, že automatické testy jsou poměrně primitivní a pokud přidáte následující test, tak většina testů projde bez zvláštního úsilí. To nicméně není účelem úlohy a nebudeme to brát jako správné řešení.
@test "gaming the tests" {
false
}
10/netcfg.py
(40 bodů, skupina devel
)
Přepište příslušnou úlohu před cvičením do jazyka Python a naučte se, jak se regulární výrazy používají v jazyce Python.
Bodové hodnocení jednotlivých testů bylo upraveno ale jinak jsou testy v podstatě totožné.
10/uname.txt
(20 bodů, skupina admin
)
Nainstalujte do počítače jazyk Ruby.
Pak na svém počítači spusťte následující skript v jazyce Ruby a vložte jeho
výstup do souboru 10/uname.txt
.
Pravděpodobně budete muset přidat správný shebang a spustitelný bit nebo explicitně spustit interpretr Ruby nad tímto zdrojovým kódem.
require 'etc'
puts Etc.uname
linux.ms.mff.cuni.cz
.
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, jak a proč je software distribuován ve formě balíčků
-
vysvětlit, co je to regulární výraz (regex)
-
vysvětlit rozdíl mezi účtem
root
a ostatními účty -
vysvětlit, proč se obecně nedoporučuje provádět neadministrativní úlohy pod účtem
root
-
obecně vysvětlit, jak lze
sudo
použít pro správu systému -
porozumět nebezpečím používání
sudo
-
vysvětlit, co je to služba (démon)
-
vysvětlit životní cyklus a možné stavy služeb
-
vysvětlit, co je to protokol programu a jak jej lze spravovat
-
vysvětlit výhody používání automatizovaných testů funkčnosti
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 …
-
vytvářet a používat jednoduché regulární výrazy na filtrování textu
grep
em -
provádět nahrazení vzoru pomocí
sed
u -
používat
getent
na získání informací o existujících uživatelských účtech -
používat
sudo
pro zvýšení oprávnění spouštěného programu -
používat správce balíčků (package manager) pro instalaci a odinstalaci balíčků
-
používat správce balíčků (package manager) pro upgrade celého systému
-
používat systemctl ke spuštění/zastavení služeb
-
používat
systemctl
pro zajištění automatického spuštění služby při startu počítače -
volitelné: používat
journalctl
k zobrazení logů -
volitelné: používat
useradd
pro vytvoření nového uživatelského účtu -
volitelné: používat SANE pro přístup ke skenerům v Linuxu
-
spouštět testy založené na BATS
-
porozumět základní struktuře testů BATS
-
volitelné: vytvářet jednoduché testy BATS
Seznam změn na této stránce
-
2023-04-14: Přidány automatické testy pro úlohy před cvičením.
-
2023-04-14: Varování ohledně návratové hodnoty grepu.
-
2023-04-20: Rozšířen příklad u hodnocené úlohy po cvičení s faktoriálem.
-
2023-04-20: Přidány automatické testy pro úlohy po cvičení.
-
2023-04-21: Další poznámka k bodované úloze o testování.
-
2023-04-26: Přepsána úloha factorial.bats task a posunutí termínu odevzdání.