Třetí série dvacátého sedmého ročníku KSP
Celý leták, který posíláme také papírově, v PDF.
- Odměna série: Řešitelům, kteří z každé úlohy získají alespoň dva body, pošleme čokoládu.
Zadání úloh
Letos se v jednotlivých sériích ohlížíme za zajímavými programátorskými
chybami, a nejinak tomu bude i dnes. V předchozích sériích jsme viděli dělení
nulou, ale také zrádnou chybu vzniklou převodem mezi celým číslem a floatem.
Dnes nás oproti tomu čeká chyba, která vznikla zejména lidským přehlédnutím
a strojovou kontrolou by byla těžko odhalitelná.
Také již opustíme válku v Perském zálivu a přesuneme se o několik let
v čase, do doby, kdy většina z vás už byla na světě. Dnešní chyba ani nebude
mít tak tragické následky, za oběť jí padlo „pouze“ několik set milionů
dolarů. Teď se ale pojďme podívat do září 1999 na Patrickovu leteckou základnu.
* * *
James zaujatě pozoroval jednu z fotografií na zdi, zatímco hučení za ním sílilo.
Když se ozvalo charakteristické cvaknutí oznamující, že voda je uvařená, vzal
rychlovarku a zalil si kávu. Kuchyňkou se rozlila typická vůně.
Vyzbrojený milovaným nápojem se James vrátil do řídicí místnosti, kde se
přidal ke svým kolegům navigátorům. Teď neměli mnoho práce, ale už za pár dní
budou jejich znalosti velmi potřeba. Blížil se totiž čas, kdy Mars Climate
Orbiter vstoupí na oběžnou dráhu Marsu.
Malé pozdvižení se ovšem dostavilo mnohem dříve. Na Zemi dorazila první fotografie
Marsu. Pravda, obraz byl zkomprimovaný a možná patřičně nepřesný, ale navigátoři
hned začali zkoumat, jestli na něm neobjeví vhodné místo k přistání.
Po Mars Climate Orbiter, který má zkoumat atmosféru Marsu z jeho oběžné
dráhy, totiž přijdou další sondy, a ty již budou na Rudé planetě přistávat.
27-3-1 Plocha k přistání (14 bodů)
Na Zemi dorazila fotografie zkomprimovaná do kvadrantového kódu. Nás zajímá,
jaké místo na ní by bylo nejvhodnější k přistání, to znamená, kde je největší
souvislá plocha.
Kvadrantový kód se používá pro dvoubarevné obrázky. Funguje tak, že se obraz
nejprve rozdělí na čtvrtiny, které se postupně zakódují (pořadí kódování čtvrtin
je „po řádcích“). Má-li celá plocha stejnou barvu (či je již tvořená jen
jediným pixelem), zakóduje se jako jedno číslo (1 pro černou nebo 0 pro
bílou barvu), v opačném případě se zpracovává rekurzivně.
Příklad takového kvadrantového kódu, který vznikl zakódováním z dvoubarevného
obrázku, připojujeme níže. Tento zápis kvadrantového kódu je konzistentní s pátou
úlohou, která ho také využívá.
1 1 0 0
1 1 0 0 ==> (10(1100)(1010))
1 1 1 0
0 0 1 0
Vaším úkolem je v kvadrantovém kódu najít největší souvislou bílou oblast.
Za sousední pixely považujeme jen ty, které spolu sousedí hranou (roh
nestačí). Počítejte s tím, že se rozkódovaný obraz nevejde do paměti (tedy
převést kvadrantový kód na obrázek a hledat oblast až v něm správné řešení
není).
Poznámka: Kvadrantový kód funguje pěkně pro čtvercové obrázky o hraně
délky nějaké mocniny dvou, ale dá se obdobně definovat i třeba pro obdélníkové
obrázky. Protože to ale nepřináší nic nového, omezíme se v řešení úlohy jen na
čtvercové obrázky o hraně délky mocniny dvou.
Řešení
James po chvíli nechal své kolegy dál zkoumat a sám se ponořil do vzpomínek …
* * *
Když bylo v srpnu 1993 jen těsně před vstupem na oběžnou dráhu ztraceno spojení
se sondou Mars Observer, a tím podstatně oddáleny šance na bližší poznání
Rudé planety, byl to šok, zvlášť pro Jamese a jeho tým.
Netrvalo ale dlouho a začaly se připravovat nové mise. Problém vesmírných misí
ovšem je, že stojí spoustu peněz, které na ně musí někdo přidělit. Za Jamesem
brzy přišel šéf, že bude potřeba napsat žádost o grant. Naštěstí tehdy dobře
věděli, na co jednotliví členové komise, která bude o schválení rozhodovat,
slyší; mohli jim tedy napsat návrh na míru. Zajímalo je ale, jakou mají vlastně
konkurenci.
27-3-2 Návrhy pro komisi (12 bodů)
Je potřeba podat návrh komisi a nás by zajímalo, kolik různých návrhů
komise schválí. Komise má C členů, kteří všichni sami za sebe rozhodují
o schválení návrhu. Jako celek pak komise návrh schválí, pokud ho schválí
alespoň K jejích členů.
Návrhy jsou ovšem dlouhé a členům se nechce číst je celé. Každý člen má
proto nějaký seznam slov, která se mu líbí, a schvaluje právě ty návrhy,
které začínají některým z jeho oblíbených slov. Návrhy i oblíbená slova
jsou řetězce složené z malých písmen anglické abecedy a navíc panuje dohoda, že
každý správný návrh má délku právě D písmen.
Na vstupu tedy dostanete počet členů komise a pro každého z nich jeho oblíbená
slova. Dále dostanete počet členů nutných ke schválení a přijatelnou délku
návrhů D. Vaším úkolem je zjistit, kolik různých návrhů (tvořených jen z malých
písmen anglické abecedy) může komisí projít jako schválené.
Zajímá nás jen počet těchto návrhů, nemusíte je generovat. Navíc se nemusíte
zabývat tím, že se vám toto číslo nevejde do běžné číselné proměnné (toto není
úloha na velká čísla).
Příklad: Uvažme trojčlennou komisi, ve které jsou potřeba alespoň
dva její členové ke schválení návrhu, a návrhy délky čtyř písmen. Oblíbená slova
jednotlivých členů vyjadřuje tabulka níže.
1. člen: pes psa
2. člen: psal kun
3. člen: pest ps
Je jasné, že návrh musí začínat na p
, jinak by ho neschválili alespoň
dva členové (na slovo kun
tedy můžeme zapomenout). Možnosti, které nám
zbývají, jsou tedy buď pest
, nebo psaX
, kde X
může být
libovolné písmeno (všimněte si, že třeba psbX
už je přijímané jen jedním
členem komise, psal
všemi a psat
alespoň dvěma).
Možností je tedy dohromady 26 + 1 = 27.
Řešení
Snad právě proto, že znali preference jednotlivých členů komise, nebylo
pro Jamesův tým těžké peníze získat. Po vyřešení finanční otázky ovšem
přišly na řadu otázky další, techničtější a v mnohém složitější.
Většinu konstrukčních záležitostí řešila společnost Lockheed Martin,
se kterou NASA uzavřela smlouvu na výrobu sondy, přesto občas některé řešené
problémy probublaly i k Jamesovi. K těm zajímavějším patřila konstrukce
antény.
Jednou z klíčových vlastností každé vesmírné sondy je totiž schopnost
komunikovat s lidmi na Zemi. Od začátku bylo jasné, že na straně Země se
k tomuto účelu využije síť Deep Space Network, která byla na Zemi
vybudovaná již koncem šedesátých let a využívá se pro komunikaci s jinými
sondami.
Aby mohla sonda do této sítě posílat informace, musí být ovšem vybavená dostatečně
silnou anténou. Taková anténa se skládá z mnoha vysílačů, jejichž volba byla trochu
oříšek. Tím spíš, že ač peníze byly, plýtvat se jimi nemohlo.
27-3-3 Výběr vysílačů (13 bodů)
Anténa vesmírné sondy má stromovou strukturu, přičemž v každém uzlu se nachází
nějaký vysílač. Kvůli rušení ale v žádných dvou sousedních uzlech nesmí být
vysílače stejných typů.
Různé vysílače mají různou cenu, i-tý typ vysílače stojí 2i dolarů (číslujeme
od 0). Na vstupu dostanete popis antény, tedy který uzel sousedí s kterým.
Určete, kolik nejméně dolarů bude stát umístění vysílačů do všech anténních uzlů.
Příklad: Na anténě níže vidíte, že v tomto případě je nejvýhodnější použít
tři typy vysílačů (s cenami 20, 21, 22 neboli 1, 2, 4). Použít jen dva
typy vysílačů by v tomto případě vyšlo dráž.
Lehčí varianta (za 3 body): Jako součást řešení vymyslete nějaký rozumně malý příklad
antény, na které je potřeba použít čtyři různé druhy vysílačů, aby výsledná cena
byla co nejmenší.
Rozumně malým příkladem nemyslíme nutně, aby měl co nejméně vrcholů to jde, ale
spíše aby byl rozumně jednoduše zkonstruovatelný (jednoduchý popis konstrukce
je lepší než obrovský obrázek o tisíci vrcholech).
Řešení
* * *
Uběhlo několik dní od chvíle, kdy Jamese hlas jednoho z jeho kolegů vytrhl ze vzpomínání
a vrátil do reality. To navigátoři museli vyměnit hledání souvislé oblasti
na fotografii za počítání, kontrolování, konzultování, nové počítání a tak
stále dokola. Sonda se totiž rychle blížila k Marsu a bylo třeba navést ji
na takovou dráhu, z které se dostane do správné výšky nad povrchem planety.
Ještě ten den spočítali vše potřebné.
O týden později, ve středu 15. září 1999, byl provedený čtvrtý manévr upravující
trasu letu. Očekávalo se, že až se sonda 23. září dostane do blízkosti Marsu,
bude se nad jeho povrchem nacházet ve výšce 226 kilometrů. Teď, tři dny před
očekávaným vstupem na oběžnou dráhu, ovšem navigátorům vycházelo, že při zachování
trajektorie bude výška mnohem menší.
James se zamračil na obrazovku počítače. Pak rychle něco naťukal do kalkulačky,
kterou měl položenou před sebou, ale stále mu vycházelo málo. 158. 158 kilometrů
nad povrchem Marsu místo očekávaných 226. To bylo o dobrou třetinu méně. Zatím to
nebylo kritické, Mars Climate Orbitter by měl s patřičnou úpravou oběžné rychlosti
přežít ještě ve výšce 80 kilometrů, ale komu by se líbilo, když se realita takovým
způsobem liší od očekávání? James se navíc děsil, že další den vyjde ještě méně.
Přitom od počátku mise probíhala dobře …
* * *
Psal se 11. prosinec 1998 a spousta lidí v čele s konstruktéry a navigátory sledovala
start nosné rakety Delta II, která měla Mars Climate Orbiter dopravit na Hohmannovu elipsu.
Mezi sledujícími James pochopitelně nemohl chybět, ačkoliv on kromě rakety důsledně
sledoval i lecjaké naměřené údaje. Po odpočtu, během kterého ještě víc vystoupalo
očekávání všech zapojených, byly zažehnuty motory. Objevil se jasný záblesk, který
přešel v ohnivou čáru, a Delta II vystřelila vstříc modrému nebi, a ještě dál.
Tak začala 669 milionů kilometrů dlouhá cesta sondy, která měla odpovědět na mnoho
otázek pozemšťanů.
Let probíhal dobře, jen v jedné chvíli navigátoři zvažovali, zda by se nevyplatilo
nechat sondu chvíli poletovat tam a zpět, aby její solární panely nasbíraly co nejvíc
energie.
27-3-4 Doplňování energie (12 bodů)
Sonda prolétá vesmírem, kde některými místy prochází výjimečně silné sluneční
paprsky. Solární panely dokáží z těchto paprsků získat energii, ovšem na přelet
mezi místy vzdálenými i spotřebuje sonda i jednotek energie. Navíc odpadní
látky zastíní paprsek, takže z jednoho místa lze energii čerpat pouze jednou.
Na vstupu dostanete popsáno, jaké množství energie se nachází v jednotlivých
místech, a vzdálenosti mezi těmito místy. Dále dostanete určený výchozí bod, na
kterém se sonda nachází. Určete, s jakou největší energií může sonda skončit.
Například pro situaci níže (horní čísla představují množství energie, dolní
vzdálenosti mezi místy) se začátkem ve třetím bodě může sonda
skončit maximálně se 64 jednotkami energie. Nejlepší řešení se z výchozího
místa vydá těmito přelety: LRRLLLRRRR (L – doleva, R – doprava).
Tato úloha je praktická a řeší se ve vyhodnocovacím systému
CodEx.
Přesný formát vstupu a výstupu, povolené jazyky a další technické informace
jsou uvedeny v CodExu přímo u úlohy.
Řešení
* * *
Jamesovy obavy nebyly plané, během dalších dvou dní klesla očekávaná výška,
v které by sonda měla k planetě přiletět, o dalších 50 kilometrů. To by ale
ještě stále mělo stačit. A dál už očekávaná výška klesat nemohla, chvíle,
kdy Mars Climate Orbiter vstoupí na oběžnou dráhu Marsu, již byla na dosah.
Právě proto bylo v řídicí místnosti rušno jako málokdy, přestože ještě nebyly ani
čtyři hodiny ráno. Blížil se jeden z převratných okamžiků kosmonautiky.
Stále nebylo jasné, proč se očekávání a realita tak rozchází. Výška, ač aktuálně
odhadovaná na málo přes 100 kilometrů nad povrchem Marsu, ovšem dostačovala a vstup
na oběžnou dráhu byl zahájen. Sonda složila své solární
panely, vhodně se vůči planetě natočila a zažehla hlavní motor.
Ve čtyři hodiny a čtyři minuty bylo spojení se sondou zničehonic přerušeno.
Navigátoři si vyměnili několik vyděšených pohledů. Snažili se obnovit kontakt,
ale nedařilo se.
Ani ne o dvě minuty později měla sonda navíc vstoupit do zákrytu Marsu, kdy
by tak jako tak nebylo možné s ní komunikovat. Nedaří se navázat spojení,
jen protože je sonda v zákrytu, nebo protože se stalo něco mnohem ošklivějšího?
Jamesovi padl pohled na fotografii, která před dvěma týdny ze sondy dorazila.
Tehdy to ještě šlo všechno skvěle!
* * *
Kdyby měla sonda lidské pocity, asi by se na své cestě dost nudila. Po zajímavém
startu a troše poletování tam a zpět za světelnými paprsky již nic zajímavého
nepřišlo. Zůstal jen dlouhý let černou tmou zpestřený pouze světly hvězd.
Po dlouhých devíti měsících sonda konečně doletěla na dohled Marsu. Ještě z velké
dálky pořídila jeho fotografii, a protože na fotografii planety z vesmíru je mnoho
tmavého místa, stejně jako mnoho světlého místa, rozhodl se počítač odeslat ji na
zemi kvadrantisticky zkomprimovanou.
27-3-5 Komprese obrazu (10 bodů)
Sonda posílá snímek Marsu. Nejprve ho ovšem za pomoci ztrátové kvadrantistické
komprese převede do kvadrantového kódu (popsaného
v 27-3-1).
Při kvadrantistické kompresi se jedna čtvrtina obrazu prohlásí za celočernou,
jedna za celobílou a zbylé dvě se zpracují rekurzivně. Pokud se rekurze dostane
až na úroveň jednotlivých pixelů, může být už barva rekurzivních částí
jakákoliv. Pro čtverec 2 ×2 ale ještě platí, že jedna jeho čtvrtina musí být
celočerná, jedna celobílá a zbylé dvě libovolné. Pořadí kvadrantů je „po řádcích“.
Na vstupu dostanete původní obraz. Vaším úkolem je vypsat kvadrantový kód
takové jeho kvandrantistické komprese, která se od původního obrazu liší
v co nejméně pixelech.
Konkrétněji bude mít vstup podobu popisu obrázku ve formátu
PBM. To je jednoduchý
formát na ukládání černobílých obrázků.
Obrázek je v něm kódovaný po řádcích,
vždy jedno číslo (1 nebo 0) na jeden pixel. Na řádku jsou mezi jednotlivými
čísly mezery a na konci každého řádku se nachází znak nového řádku, nic jiného
se zde nevyskytuje. Platný PBM soubor je také uvozen na prvním řádku
znaky P1
a na druhém řádku mezerou oddělenými čísly udávajícími jeho šířku
a výšku (v tomto pořadí).
Obrázky v této úloze budou pro jednoduchost vždy čtvercové o hraně 2K pixelů
a jejich velikost nepřesáhne 1024×1024 pixelů.
Na výstup vypište nejprve na první řádek počet změněných pixelů a následně
na druhý řádek kvadrantový kód kvadrantistické komprese.
Ukázkový vstup:
P1
8 8
1 1 1 1 1 1 1 0
1 1 1 0 1 1 1 0
1 0 0 0 1 1 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0
Ukázkový výstup:
8
((1(1110)(1000)0)
(1(1010)(1110)0)
1
0
)
Poznámka k příkladu: Pro přehlednost příkladu jsme druhý řádek výstupu rozlomili po
jednotlivých kvadrantech, v reálném výstupu by vše od první do poslední závorky
bylo na jediném řádku.
Ukázkový vstup:
P1
4 4
0 0 1 1
0 0 1 1
0 0 1 1
1 0 1 1
Ukázkový výstup:
1
(01(0010)(0111))
Toto je praktická open-data úloha. V odevzdávátku
si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.
K prohlédnutí obrázku ve formátu PBM můžete využít na Linuxu např. program Eye of Gnome (eog),
na Windowsech programy Irfan View nebo XnView. Na obou systémech si s PBM poradí i Gimp či
OpenOffice Draw.
Řešení
* * *
Jamesova myšlenka, že tehdy to ještě šlo skvěle, se časem ukázala jako
nepříjemně přesná.
Mars Climate Orbitter se totiž navigátorům neozval nejen po dvaceti minutách, kdy se měl
opět dostat mimo zákryt Marsu, ale ani po hodině, ani po dvou dnech.
Po těchto dvou dnech byla sonda oficiálně prohlášena za ztracenou a mise za neúspěšnou.
Navigátoři zpětně spočítali, že sonda se ve skutečnosti dostala do výšky pouhých
57 kilometrů nad povrchem Marsu, kde ji zřejmě spálila atmosféra.
Ještě před oficiálním ukončením mise bylo zahájeno vyšetřování s cílem zjistit,
co se vlastně stalo a proč se sonda pohybovala mnohem níž, než všichni očekávali.
James si rychle zvykl, že se teď kolem něj pohybuje mnohem víc lidí, že se zkoumá
hned tu, hned ono. I jeho samotného zajímala příčina tohoto selhání a snažil se
přijít věci na kloub.
Do místnosti právě vešel i jeden z techniků. „Hej, lidi, pomůžete mi někdo
uklidit přepravky ve skladu?“ ptal se hned místo pozdravu. James usoudil, že trocha
fyzické aktivity mu jen prospěje a přidal se k několika ochotným pomocníkům.
27-3-6 Ukládání přepravek (9 bodů)
Ve skladu je třeba uspořádat přepravky, a to tak, aby zabíraly co nejméně místa.
Přepravky jsou kulaté, každá má svůj vnější a vnitřní průměr. Pokud je vnější průměr
jedné přepravky menší než vnitřní průměr druhé přepravky, dají se vložit do sebe
(a do nich případně ještě menší přepravka, vznikají tak jakési „komínky“).
Na vstupu dostanete vnitřní a vnější průměry všech N přepravek. Vaším úkolem je
zjistit, do kolika nejméně komínků se dají uspořádat.
Řešení
„Tedy, tohle bude mít pěkných pár liber,“ prohlásil jeden z pomocníků zvedaje
pořádný komínek mnoha přepravek.
„Cos to řekl?“ vytřeštil oči Jamesův kolega Thomas.
„Jen že je to těžké…“ bránil se pomocník.
Ostatní, včetně Jamese, Thomase jen nechápavě pozorovali.
„O to nejde. Jde o ty libry! A o to, že libry nejsou kilogramy,“ pokračoval
vzdor nechápavým pohledům Thomas. „A taky o to, že kilogramy jsou to, co ta
sonda očekávala.“
Ozvala se hlasitá rána. To Jamesovi z rukou vypadlo několik přepravek. A podle
výrazů ostatních byla spíš náhoda, že se to samé nestalo více lidem.
* * *
Vyšetřování potvrdilo, že příčinou selhání byla neshoda v používaných jednotkách.
Řídicí středisko ze Země odesílalo instrukce s imperiálními mírami, kdy sílu
udávalo v silových librách. Sonda je ovšem očekávala v metrické podobě, tedy
sílu čekala v Newtonech.
Jelikož silová libra je více než čtyřnásobek Newtonu, došlo při výpočtech
k chybám, které byly pro úspěšnost vstupu na oběžnou dráhu fatální. Rozkol mezi
očekávanou a naměřenou pozicí byl zaznamenaný a v týmu zodpovídajícím za let
družice se uvažovalo o provedení ještě dalšího, pátého, manévru korigujícího
dráhu, ten ale nebyl nikdy provedený.
Neúspěšnou misi s vámi sledovala
Karolína „Karryanna“ Burešová
27-3-7 UNIXové déjà vu (15 bodů)
Dnešní díl seriálu ve vás možná vyvolá pocit, že jste ho už někdy četli, ale ne
tak docela. Vrátíme se totiž k mnohému z toho, co už umíte, a pronikneme ještě
o kousek hlouběji. Připravte se na vydatnou porci povídání o nápovědě,
o souborovém systému, uživatelích a právech, o řídicích strukturách a funkcích
v shellu a o formátovaném výstupu.
Z předchozích dílů seriálu máte k dispozici shell, nejspíš Bash, umíte se v něm
pohybovat po souborovém systému a zvládáte psát jednoduché skripty pro
manipulaci s obsahem textových souborů. Také když zapomenete přepínače
konkrétního příkazu, umíte si je v manuálových stránkách najít.
Umíte si ale pomoct, když zapomenete, jak se nějaký příkaz jmenuje?
Nápověda
Je nemožné si pamatovat všechny vlastnosti každého nainstalovaného programu,
natož stíhat sledovat změny. Nápověda, manuál nebo dokumentace jsou základními
prameny informací pro uživatele libovolného software a UNIX v tomto ohledu není
výjimkou. Sebelepší informace je ale k ničemu, když ji neumíte najít.
Příkaz man
už znáte. Napadlo vás podívat se na man man
?
Zjistíte tam, že k hledání řetězců v popisech příkazů slouží přepínač -k
.
Pokročilejší možnosti nabízí utilita apropos
.
Také narazíte na přepínač -s
, jehož hodnotou je sekce manuálu a někdy jde
dokonce přepínač vynechat a psát jen sekci (man 1 man
). Počkat, co jsou
sekce? Jsou očíslované, každá stránka je v nějaké zařazena a obvykle ji má
uvedenou v závorce za svým jménem (např. cat(1)
, shells(5)
nebo
standards(7)
). Konkrétní význam a číslování se liší, POSIXový standard
(který si zmíníme dále) o nich nemluví vůbec. Pro představu se podívejme na
Debian Linux:
1 | Spustitelné programy nebo příkazy shellu |
2 | Systémová volání (funkce poskytované jádrem) |
3 | Knihovní volání (funkce v programových knihovnách) |
4 | Speciální soubory (obvykle nalézané v /dev ) |
5 | Souborové formáty a konvence (např. /etc/passwd ) |
6 | Hry |
7 | Směs (včetně balíků maker a konvencí) |
8 | Příkazy administrace systému (obvykle jen pro roota) |
9 | Funkce jádra |
V různých sekcích můžete najít stejnojmenné stránky. Například passwd(1)
je utilita pro změnu hesla a passwd(5)
(sekce 5 na Linuxu, jinde
nejspíš jiná) dokumentuje databázi uživatelů /etc/passwd
. Musíte buď
sekci znát, nebo použít přepínač -a
a postupně si prohlédnout všechny.
Když už konkrétní stránku máte, pořád není vyhráno. Může být dlouhá
a nepřehledná. Pak vám pomůže váš stránkovač. Příkaz man
by mohl vysypat
horu textu přímo do terminálu se škodolibým úsměvem a slovy „poraďte si“,
jako příjemnější se ale ukázalo, když pustí less
nebo starší a standardní
more
a manuál vám ukáže v něm. V prvním dílu jsme vám prozradili, že se
oba zavírají klávesou q
(quit), přidáváme h
(help) pro nápovědu,
/
(lomítko) pro vyhledávání (potvrdíte enterem) a n
(next) pro
vyhledání dalšího výskytu.
Aby to nebylo příliš jednoduché, k manuálu existuje alternativa: infostránky.
Některé jsou mnohem obsáhlejší než odpovídající manuálová stránka a jsou
členěné, nevypadají jako jeden dlouhý dokument. Jejich prohlížeč se jmenuje
info
, nápovědu v něm získáte napsáním otazníku, zbytek už zjistíte sami.
Bash k nápovědě přistupuje po svém. Na man bash
najdete i popis jeho
vestavěných příkazů, jako je cd
nebo pwd
, kdo by ovšem chtěl
hledat jehlu v kupce sena? Vysvobodí vás jeho příkaz help
. Mrkněte na
help help
, je vcelku intuitivní.
Pokročilejší z vás by mohlo zajímat, které utility a jejich přepínače mají být
dostupné na všech UNIXech, ať už je to Gnu/Linux, Solaris, OS X nebo nějaká
odnož BSD. Taková znalost slouží k psaní přenositelných skriptů, tedy skriptů,
které budou fungovat i na jiném systému, než na kterém jste je napsali. Vaši
zvědavost ukojí norma POSIX, které se certifikované UNIXy držet musejí a ty
ostatní aspoň plus minus chtějí. Kdykoliv se budeme odvolávat na normu nebo
POSIX, myslíme POSIX 2013.
Na jeho stránce je vpravo dole odkaz ke stažení té kupky HTML stránek v jednom
archivu, z neoficiálních zdrojů je možné sehnat POSIX i v podobě manuálových
stránek. V Debianu je takovým zdrojem balík manpages-posix
v repozitáři
non-free.
Souborový systém
První díl seriálu se vás snažil nezahltit a nerozptylovat, o souborovém systému
řekl jen to nejnutnější, minule jste nakoukli do práv souborů, když jste
vytvářeli spustitelný skript. Je načase povědět o souborovém systému víc.
Logicky je souborový systém jediný, s kořenem /
(„root“), fyzicky jich
ale bývá víc, z nichž některé mohou sídlit třeba jen v operační paměti nebo
dokonce na úplně jiném stroji. Všechny dostupné na aktuálním stroji si můžete
prohlédnout příkazem df
. Vypíše pro každý souborový systém do tabulky
název, velikost, využití a kam v logickém souborovém systému je připojený. Často
je k dispozici přepínač -T
, se kterým df
ukáže i typ souborového
systému, a přepínač -h
, se kterým vypíše obsazené a volné místo v lidsky
čítelných jednotkách.
Prostor zabraný konkrétním souborem umí spočítat du
. Pokud dostane
adresář, rozpitvá statistiku na jeho položky, podobně jako je tomu u ls
.
Příkazu ls
to můžete zakázat přepínačem -d
, obdobně
u du -s
.
Nenechte se zmást tím, že df
i du
přemýšlejí v blocích. Je to dáno
běžnou strukturou disků, soubor zabírající blok jen z části nemůže jeho zbytek
přenechat jinému souboru, přebytečné místo zůstane nevyužité. Velikost bloku se
obvykle liší mezi normou, utilitami a diskem, buďte tedy obezřetní a uvědomujte
si, jaká velikost se u vás kde používá.
Příkaz ls
s přepínačem -l
zobrazuje velikost souboru v bajtech a
na zabrané bloky se nijak neohlíží. Počet zabraných bloků nechá zobrazit
přepínač -s
. Celkovou velikost zabraných bloků v lidsky čitelných
jednotkách ukazuje du -h
.
Úkol 1 [1b]
Zjistěte, jak velké bloky používá váš disk a vaše utilita du
, která by
podle normy měla používat 512B bloky. Svá zjištění doložte použitými příkazy,
jejich výstupy a popisem své úvahy.
Konkrétní použitý souborový systém s sebou nese svá omezení. Na Windows se kdysi
používal formát FAT, později NTFS, v Linuxu jsou doma ext2 až ext4, v BSD
a Solarisu ufs. Lišit se mohou maximální délkou jména souboru, maximální
velikostí souboru, maximální využitelnou velikostí disku, povolenými znaky
v názvech souborů, (ne)podporou ukládání různých metadat, …
Norma vyžaduje, aby souborový systém rozlišoval malá a velká písmena
a názvy souborů neobsahovaly lomítko (oddělovač komponent cesty) a NUL
(\0
v jazyce C, bajt s hodnotou 0). Jazyk C vznikl pod UNIXem a UNIX do
něj byl po čase přepsán, jsou spolu dodnes hodně prolnuté. Když zakážeme znak
NUL, máme zaručeno, že je možné název souboru považovat za řetězec jazyka C
a používat na něm řetězcové funkce, např. strlen()
.
Výše uvedená omezení jsou dnes běžně opravdu jediná vynucená, všechno ostatní
funguje. (Nepočítáme-li obskurní starožitný FAT, přežívající na
některých flashdiscích.) Čímž neříkáme, že celý zbytek Unicode v názvech souborů najdete, nebo dokonce že
můžete obskurními znaky soubory beztrestně pojmenovávat.
Díky absenci NUL v názvu souboru máme jisté, že když za sebe naskládáme jména
souborů oddělená znakem NUL, budeme je umět opět jednoznačně rozdělit. Toho
využívají některé běžné utility, bohužel pomocí nestandardních přepínačů. Tuto
jistotu nám norma nedává, pokud použijeme jako obvykle znak LF (\n
v C,
„konec řádku“).
Proto se z bílých znaků používá jen mezera, LF v názvu naštěstí není běžné,
tedy můžeme být v klidu. Kdo takovou zvyklost poruší, následky nechť si nese
sám. Zkuste si nějaký soubor s LF v názvu vyrobit a pohrát si s ním!
Soubory s diakritikou, interpunkcí a mezerami v názvech se opravdu dají potkat,
takže by s nimi vaše skripty měly umět pracovat. Pomohou vám k tomu znalosti
z prvního dílu: escapování, uvozovkování a ve vzácném případě minusu na začátku
názvu souboru parametr --
.
Soubory systému a programů obvykle dodržují ještě mnohem striktnější omezení,
než je to popsané výš. Volí jména souborů, která
- obsahují jen písmena velké a malé anglické abecedy, číslice, podtržítko,
tečku a minus (
[A-Za-z_.-]
), a navíc
- nezačínají minusem (aby se nepletla s přepínači).
Soubory s tečkou na začátku jsou skryté před wildcardy a
ls
. Druhé běžné
použití tečky je oddělení přípony od zbytku jména souboru, jinak se tímto znakem
šetří.
Shell se hodí na jednoduché skripty spořící čas, ne na psaní neprůstřelných
programů (to v něm ani dobře nejde). Pokud v něm budete pracovat víc,
dojdete ke kompromisu mezi omezováním se a nutností uvozovkovat při běžné práci
moc často. Vynecháte nejspíš běžné oddělovače (LF, mezeru, tabulátor, dvojtečku,
středník a čárku),
speciální znaky shellu ($`"'#!?*[]{}();|{\I }&
), a dáte si pozor na minus na začátku názvu. Možná si navíc budete šetřit čas při
psaní vynecháním diakritiky a velkých písmen, ačkoliv to zas tolik nepomůže.
Důležité je hlavně trefit začátek slova a zbytek už zařídí doplňování
tabulátorem.
Části cesty, přípony
Přípony. Ve Windows se podle nich točí svět, binárku bez přípony .exe
spustíte těžko. UNIX se s nimi vypořádal jinak – přípony považuje za informaci
pro uživatele, sám se řídí prvními několika bajty souboru, kde obvykle
je „magic number“. Podle něj umí formát určit třeba i utilita file
:
hroch@ksp:~$ file /etc/passwd
/etc/passwd: UTF-8 Unicode text
hroch@ksp:~$ file /usr/bin/vimtutor
/usr/bin/vimtutor: POSIX shell script text executable
U binárních programů toho file
umí zjistit hodně. Kdybychom ho neměli,
museli bychom binárku prohlížet nějak ručně a třeba si všimnout toho, že na
začátku jsou znaky DEL, E, L a F, přičemž ELF je jméno formátu spustitelných
souborů pro UNIX.
hroch@ksp:~$ od -c -Ax -tx1 -N10 /bin/sh
000000 177 E L F 002 001 001 \0 \0 \0
7f 45 4c 46 02 01 01 00 00 00
00000a
Zkuste si sami pomocí od
nebo rozšířeného, ale nestandardního hd
prohlédnout nějaký obrázek PNG, dokument PDF, … Pokud chcete být drsní,
vynechte u od
přepínač -c
a ve vedlejším terminálu si otevřete
man ascii
. ;-)
Příponu tedy běžně není potřeba od zbytku jména souboru oddělovat, obzvlášť
u textových a spustitelných souborů často ani žádná přípona použita není. Zato
bychom někde ve skriptu mohli chtít získat zvlášť jméno souboru a zbytek cesty.
Poslouží nám příkazy basename
a dirname
:
hroch@ksp:~$ dirname /usr/bin/less
/usr/bin
hroch@ksp:~$ basename /usr/bin/less
less
Jejich opakovaným použitím můžete rozebrat cestu na jednotlivé komponenty.
Typy souborů
Když už jsme nakousli soubory v UNIXu, podívejme se na ně blíž. UNIXová
filosofie se totiž drží zásady, že skoro všechno je soubor. Běžné textové
soubory nebo soubory s binárními daty (fotky, videa, …) nás asi nepřekvapí.
Ale UNIX jako soubory reprezentuje i takové věci jako vstup z klávesnice
(systém odtud čte po znacích) nebo výstup do zvukové karty. Podstatné je, jak
se který soubor chová při používání.
Soubor je na disku typicky reprezentován jedním inodem,
každý z nich má v rámci souborového systému svoje unikátní číslo. Uvnitř mezi
dalšími metadaty systém ukládá informace o právech a vlastnících souboru, jeho typ
a velikost, počítadlo odkazů (viz dále) a hlavně odkazy na jednotlivé datové
bloky se samotným obsahem.
Je důležité, že jméno souboru si nepamatuje sám soubor, ale pamatuje si ho
nadřazený adresář (což je jen speciální typ souboru). Inode reprezentující
adresář obsahuje ve své datové části jména a příslušná čísla inodů pro všechny
v něm obsažené soubory.
V předchozích dílech jsme se věnovali jen dvěma typům souborů: běžným souborům
a adresářům. Dalšími jsou již zmiňovaná vstupní a výstupní zařízení, která sídlí
hlavně v adresáři /dev
a dělí se na bloková (disk) a znaková (terminál).
V neposlední řadě se hodí vědět o rourách.
Zatím jsme potkali jen roury anonymní, které shell natahuje mezi dvěma
příbuznými (společně spouštěnými) procesy: ls -l /bin | head
. Mezi
nepříbuznými procesy (spouštěnými třeba i dvěma různými uživateli) anonymní
rouru natáhnout nejde, ale oba mohou znát cestu k pojmenované rouře. Jeden z ní
čte, druhý do ní zapisuje a jméno potřebují jenom k tomu, aby ji mohli otevřít.
A kde pojmenovanou rouru sebereme? Vytvoří ji příkaz mkfifo
.
V shellu je bezesporu nejpoužívanějším speciálním souborem /dev/null
neboli „černá díra“. Je to ideální místo, kam zahazovat věci, které na nic
nepotřebujeme. Můžeme ho využít, pokud nás nezajímá standardní výstup příkazu,
a jde nám jenom o jím vyrobený soubor nebo jeho návratovou hodnotu. Pokud bude
příkaz chtít vstup a my mu budeme chtít dát prázdný soubor, /dev/null
je
také vhodné použít.
echo Windows > /dev/null
cat /dev/null
Podobně se chová /dev/zero
, až na to, že při čtení dodává nekonečně
dlouhou posloupnost znaků NUL. Soubor délky 1024 bajtů vytvoří head -c 1024 /dev/zero > soubor
. Pěkné hraní je i se soubory
/dev/random
a /dev/urandom
.
Někdy přesměrujeme našemu skriptu výstup do souboru, a přesto bychom chtěli
vybrané informace o jeho provádění vidět na terminálu. Může nám je ukazovat tak,
že je bude zapisovat do /dev/tty
, který reprezentuje aktuální terminál.
Podobně když máme přesměrovaný vstup a chceme z terminálu (z klávesnice) číst.
(
echo 'Potvrdte "ano":' > /dev/tty
read odpoved < /dev/tty
[ "$odpoved" = ano ] || exit 1
cat
) < soubor1 > soubor2
Málem bychom zapomněli… Ve výstupu ls -l
poznáte jednotlivé typy
souborů podle prvního znaku na řádku, ještě před právy. Kdyby nějaké písmeno
nebylo jasné, v manuálu ls
jsou zkratky vysvětleny. Minus je běžný
soubor.
Odkazy v souborovém systému
Občas se nám hodí pořídit si zkratku, rychlý odkaz na nějaký soubor či adresář.
Na takové věci se v UNIXu využívají hardlinky (linky) a symlinky.
Hardlink je odkaz, který „natvrdo“ ukazuje na stejný inode jako odkazovaný
soubor. V adresáři je pod jménem linku uloženo přímo číslo inode, na který
odkazuje. Při vytvoření linku se v daném inode zvedne počítadlo odkazů, při jeho
smazání se zase sníží, a když dojde na nulu, odstraní se i samotná data.
Výjimkou jsou otevřené soubory – dokud nějaký proces má soubor otevřený,
souborový systém přepsání jeho dat neumožní. Tak může mít proces bezpečně
otevřený i soubor, který už nemá žádné jméno.
Po vytvoření hardlinku jsou původní a nový soubor od sebe nerozeznatelné, ale
nese to s sebou několik omezení. Předně musí všechny linky sídlit na stejném
diskovém oddílu, kde jsou uložena samotná data, a také nejde vytvořit hardlink
na adresář, jen na soubor. (Kdo by se vyznal v souborovém systému, kde může být
adresář umístěný sám v sobě?) Při přesunutí (přejmenování) nebo vymazání
původního souboru bude hardlink ukazovat stále na původní verzi. Protože mohou
být hardlinky někdy zrádné, doporučujeme pro běžnou práci používat spíše
symlinky.
Symlink, neboli symbolic link, je jen jednoduchý zástupce, který ukazuje na
nějakou cestu v souborovém systému (na libovolném připojeném oddílu a na
libovolný soubor či adresář). Ve skutečnosti vypadá skoro jako malý textový
soubor, který má v sobě zapsanou absolutní (začínající lomítkem) či relativní
cestu ke svému cíli.
Při vymazání nebo přesunutí původního souboru přestane symlink fungovat, pokud
neobsahuje relativní cestu a není přesunut spolu s cílem. Ukazuje totiž na
cestu, ne na konkrétní soubor. Pokud cílový soubor smažeme a nahradíme novým,
bude symlink ukazovat na tento nový soubor.
K symlinkům je třeba dodat dvě varování, abyste se nedivili a nedomýšleli si
něco, co není pravda.
- Předně, symlink a zástupce z Windows se chovají zásadně jinak. Zástupci jsou
běžné soubory, podobné
.desktop
souborům na Linuxu, jen jsou binární.
- Druhé varování se týká výstupu
ls -s
. Na mnoha systémech bude
u symlinků ukazovat nulový počet zabraných bloků. Pokud je totiž symlink
dostatečně malý, není problém ho celý uložit přímo uvnitř jeho inode. Jak úsporné! :-) Říká se tomu inlining a nové souborové systémy (třeba ext4)
umí toto chování zapnout i u jiných typů souborů.
Hardlinky a symlinky se vytvářejí příkazem ln
. Bez přepínače vyrobí
hardlink, s přepínačem -s
symlink:
ln cesta/k/souboru novy_hardlink
ln -s cesta/k/souboru relativni_symlink
ln -s /cesta/k/souboru absolutni_symlink
Cíl symlinku běžně vyčtete z ls -l
:
lrwxrwxrwx 1 hroch users 3 8. pro 08:00 zdroj -> cil
Ve skriptech se nám bude hodit spíš příkaz readlink
, který vypíše jenom
cíl odkazu. Také má užitečný přepínač -f
, se kterým vypisuje absolutní
cestu získanou z argumentu nahrazováním všech symlinků na cestě skutečnými
cestami, kam ukazují. Příkazu readlink -f
tedy má smysl dávat nejenom
symlinky, ale i běžné soubory.
Pokud budete uvnitř adresáře, do kterého jste se dostali přes symlink, můžete si
plnou cestu k němu nechat vypsat pomocí pwd -P
, přepínač -P
zajistí rozepsání všech symlinků v cestě. Chová se stejně jako
readlink -f .
(všimněte si použití tečky).
Úkol 2 [2b]
Pusťte si ls -ld
na nějaký adresář a všimněte si, kolik má hardlinků.
Odkud vedou?
Úkol 3 [2b]
Vysvětlete, jak se v souvislosti s hardlinky liší
cat </dev/null >soubor
a
rm soubor
cat </dev/null >soubor
Uživatelé, skupiny, vlastníci a práva
Teď už máme představu o tom, co všechno v souborovém systému můžeme najít. Zatím
jsme v něm ale beznadějně sami. Sotva přijdou další uživatelé, potřebujeme před
nimi občas něco schovat, nenechat je měnit naše soubory, … Tedy budeme
potřebovat systém oprávnění, který jsme v minulém dílu načali právem ke
spuštění. Vůbec jsme ale nemluvili o tom, komu chmod +x
právo ke spuštění
přidělí.
UNIX je odpradávna víceuživatelský systém. Oddělené účty uživatelů
a dodržování principu minimálních oprávnění mu zajišťují slušnou míru
bezpečnosti. Soubor /etc/passwd
obsahující databázi uživatelů už jste
potkali, jeho dokumentaci najdete na Linuxu v man 5 passwd
, jinde musíte
hledat v jiné sekci. Možná vás zajímalo, kde se ukládají hesla – pak vězte, že
v dřevních dobách to bylo opravdu tam, ale moderní UNIXy mají na citlivé údaje
oddělenou databázi jinde v adresáři /etc
. Hesla se navíc ukládají
zakódovaná. Na Linuxu vám víc prozradí man shadow
.
Vžijte se na chvíli do role administrátora školního UNIXového serveru. Máte na
něm účty desítek, možná i stovek uživatelů a chcete jim přidělit nějaká
oprávnění (jaká, k tomu se dostaneme později). Chtělo by se vám u každého zvlášť
přemýšlet, co smí a nesmí? Asi byste si všimli, že studentské účty mají mít
obecně jiná oprávnění než účty učitelů, že dokumenty k maturitnímu plesu jedné
třídy nemá co upravovat nikdo z jiných tříd, tedy pokud nemají dvě třídy ples
společný, atd. Přáli byste si, abyste mohli systému o třídách a učitelích říct
a on vám dovolil oprávnění přidělovat hromadně.
Přání se vám splnilo, řešení má jméno skupiny. Jsou to pojmenované množiny
uživatelů, je jim možné nastavovat společná práva, jejich databáze sídlí
v /etc/group
a více si o ní můžete přečíst v man group
.
V /etc/passwd
má každý uživatel navíc skupinu, pod kterou se přihlašuje,
neboli login group. Do ní automaticky patří.
Co znamená, že se uživatel pod nějakou skupinou přihlašuje? Inu, tato skupina je
skupinovým vlastníkem jím vytvářených souborů. Co to pro ně znamená?
Když se podíváte na výstup ls -l
, uvidíte řádky jako:
-rwxr-x--x 1 hroch users 42 8. pro 08:00 soubor
Uživatelským vlastníkem souboru soubor
je hroch
, skupinovým je
users
. Pokud se řekne jenom vlastník, myslí se uživatelský vlastník.
Nenechte se zmást, až někde uvidíte hroch
v obou sloupcích – skupiny
a uživatelé se mohou jmenovat stejně a na některých systémech se takto pro
každého uživatele zakládá jeho vlastní login group.
Na uživatelského vlastníka (user, owner) se vztahuje jiné nastavení oprávnění
než na toho skupinového (group, group owner), a na toho zase úplně jiné než na
všechny zbývající uživatele (others, world) kromě uživatele root
.
Root, nebo také superuživatel, je takovým místním bohem. Smí všechno. Jeho
oprávnění je potřeba např. k zakládání nových účtů nebo ke změně vlastníka
souboru. Změnit skupinu souboru na některou ze skupin, ve které je členem, může
ale i vlastník souboru.
Tolik pravomocí s sebou nese i hodně zodpovědnosti a moc velký průšvih, když se
k účtu roota dostane někdo nepovolaný. Přestože se hledají alternativní cesty,
jak si poradit bez superuživatele, root s námi ještě nějaký čas bude.
Pomocí příkazu su
můžeme pouštět příkazy rootovým jménem, nebo i jménem
jiného uživatele, pokud známe jeho heslo. Pokud si pustíme rootovský shell, bude
mít v promptu místo dolaru mřížku (#
). Alternativou su
je příkaz
sudo
, který nám dovoluje pouštět příkazy pod jiným vlastníkem po zadání
k tomuto účelu speciálně nastaveného hesla.
Pokud root zrovna nejste a snažíte se přistupovat k souboru, jaká pro vás platí
oprávnění? Ve výše uvedeném příkladu
-
rwx
pokud jste hroch
, jinak
-
r-x
pokud jste ve skupině users
,
-
--x
ve všech ostatních případech.
Tento symbolický zápis práv není až tak spletitý, jak na první pohled
vypadá. První pozice v každé trojici je Read (čtení), druhá Write
(zápis), třetí eXecute (spuštění). Minus znamená, že právo není přiděleno.
Při čtení práv ve výstupu ls -l
nezapomeňte, že těsně před nimi je typ
souboru. Připomínáme, že minus znamená běžný soubor.
Často se používá ještě druhý způsob zápisu práv, numerický, nebo také oktalový, tedy pomocí osmičkové číselné soustavy. Práva vlastníka, skupinového
vlastníka a ostatních v něm jsou reprezentovaná třemi osmičkovými ciframi. Jedna
osmičková cifra má tři bity. 4 = r
, 2 = w
, 1 = x
. Skládání se
dělá bitovým součtem (or). 000 jsou tedy doslova nulová práva, 777 všechno všem,
700 všechno vlastníkovi, 755 všechno vlastníkovi a ostatním jen čtení
a spouštění, 640 čtení a psaní vlastníkovi, čtení skupině a nic ostatním.
U běžných souborů jsou práva téměř intuitivní. Drobné zádrhele mohou být, že
interpretované programy potřebují kromě hashbangu (např. #!/bin/bash
,
vizte předchozí díl seriálu) a práva ke spuštění i právo ke čtení. Interpret se
k textu programu holt nějak musí dostat. U binárek problém není. Naopak binárky
nespustíte bez práva ke spuštění, kdežto u skriptu v Bashi si snadno poradíte
zavoláním bash skript.sh
.
Větší potíže bývají s významem práva k zápisu. My jsme si ale už vysvětlili, jak
fungují linky na soubor a že data souborů a záznamy v adresářích jsou na sobě do
značné míry nezávislé, takže to máme snazší. Právo k zápisu mluví u souboru
o zápisu do jeho dat. K přejmenování nebo smazání se vztahuje právo zápisu do
adresáře. Přesto utilita rm
váhá, když má mazat soubor, který nemá právo
k zápisu. Je potřeba smazání ručně potvrdit nebo předem přidat přepínač
-f
(force – „Na nic se neptej a maž!“).
Práva souboru se ukládají s jeho inode – pokud je změníme přes jeden link,
projeví se změna i ve všech ostatních. Zde se hodí třetí varování k symlinkům:
práva u nich nemají smysl. Symlink se často chová transparentně a rozhodující
jsou práva cílového souboru. Nenechte se zmást tím, že ls
mu přisuzuje
práva 777.
U adresářů jsme už právo k zápisu vyřídili o dva odstavce výš. Právo ke čtení
adresáře potřebují ke své správné funkci wildcardy, ls
, doplňování
tabulátorem a vůbec cokoliv, co potřebuje vidět obsah adresáře. Právu x
se
u adresářů říká search (prohledávání). Dovoluje nám se znalostí jména souboru
přistoupit k jeho obsahu, tedy při hodně nízkoúrovňovém pohledu vlastně přečíst
z adresáře číslo inode souboru, pokud dodáme jeho jméno.
Bez práva ke čtení, ale s právem k prohledávání můžeme jaksi „poslepu“
pracovat s obsaženými soubory známých jmen, a v závislosti na právu k zápisu je
můžeme i vytvářet a mazat. S právem ke čtení, ale bez práva k prohledávání můžeme
obsah adresáře jenom vypsat, a to ještě mizerně. U každé položky uvidíme
v ls -l
jen jméno (a na některých souborových systémech i typ), ostatní
metadata jsou uvnitř inode a místo nich
se zobrazí jen otazníky. Ani nám pak nebude vadit, že bez práva k prohledávání
nemůžeme nastavit adresář jako svůj pracovní (cd adresar
).
Když už víme, k čemu práva jsou, jak je změnit? Příkaz chmod
už jsme
párkrát zmínili. Teď už navíc budete rozumět jeho manuálu, kde se můžete dočíst
pikantní podrobnosti o právech včetně těch, které se sem nevešly.
Příkaz chmod
může pracovat s právy zadanými oktalově i symbolicky. Se
symbolickými právy umí nastavovat i jednotlivá práva při zachování ostatních.
Kombinací je docela hodně, uveďme tedy jen příklad:
chmod u+x,go-rw soubor
přidá uživateli právo ke spuštění a skupině
i ostatním sebere práva ke čtení a k zápisu, ostatní práva nechá netknutá.
Vlastníka umí měnit příkaz chown
, skupinového chgrp
. Příkaz
chown
navíc umí měnit skupinového i uživatelského vlastníka najednou,
stačí je zadat oddělené dvojtečkou:
ksp:~# ls -l s; chown palec:ksp s; ls -l s
-rw-r--r-- 1 hroch users 1 8. pro 08:00 s
-rw-r--r-- 1 palec ksp 1 8. pro 08:00 s
Pokud u chown
vynecháme uživatele před dvojtečkou, změní jen skupinového
vlastníka. Příkazy chmod
, chown
i chgrp
mají přepínač
-R
, který je nechá změnu aplikovat na daný adresář a rekurzivně na
všechen jeho obsah.
Když už soubory existují, poradit si s nimi umíme. Teď si povíme, s jakými právy
a vlastníky se zakládají.
Stejně jako soubor, i každý proces má vlastníka. Vlastník procesu se použije
jako vlastník souborů tímto procesem vytvářených. Se skupinovým vlastníkem je to
složitěšjí, ten se občas může dědit od adresáře. Přesný algoritmus výběru závisí
na systému a jeho nastavení.
Náš shell má jako vlastníka nás a jako skupinového vlastníka naši login group.
Procesy, které spouští, tyto vlastníky zdědí. Všechny „běží pod námi“.
Výše zmíněné příkazy su
a sudo
umožňují ovlivňovat vlastníka,
o skupinového vlastníka se postará newgrp
. Informace o aktuálním
uživateli, skupině a ostatních skupinách, ve kterých uživatel je, poskytuje
příkaz id
.
Výchozí práva běžných souborů jsou 666, výchozí práva adresářů 777. Při
vytváření se ale aplikuje maska, která zruší v přidělovaných právech ty bity,
které jsou v ní nastavené na jedničku. Maska je stejně jako vlastník a skupina
vlastností procesu a stejně jako oni se dědí. U shellu ji můžeme zjistit
příkazem umask
bez parametrů a nastavit stejným příkazem, kterému předáme
novou masku jako argument. Typické nastavení je umask 002
(„nedávej
w
celému světu“) nebo umask 022
(„w
nech jen
vlastníkovi“).
Úkol 4 [3b]
Jak root zařídí, aby domácí adresář uživatele hroch nebyl přístupný ostatním a
aby na tom hroch nemohl nic změnit? Dodejme, že je dobrým zvykem, aby domácí
adresář patřil příslušnému uživateli.
Řídicí struktury a proměnné – podruhé na návštěvě
Ve druhém dílu seriálu jsme se potkali se základními řídicími strukturami. Pokud
si nepamatujete použití podmínky if
nebo dvou základních cyklů níže,
připomeňte si je.
if cmd; then ...; else ...; fi
while cmd; do ...; done
for i in 1 2 3 4 5; do ...; done
Dnes k těmto základům přidáme mocnější zbraně. Nejdříve si pořídíme na hraní
nějký nekonečný while
cyklus, tedy cyklus, jehož podmínka bude vždy
splněná. Abychom nemuseli psát podmínku stylu „nula je menší než jedna“,
nabízí nám shell příkazy true
a false
.
Ano, nepřepsali jsme se, nejsou to konstanty nabývající hodnoty pravda
a nepravda jako ve většině programovacích jazyků. V shellu se jedná
o samostatné příkazy, které doslova dělají nic, a to buď úspěšně, nebo
neúspěšně. Podívejte se na jejich manuálové stránky.
Náš nekonečný cyklus, ve kterém si třeba budeme k proměnné X
na konec
připisovat D
a to celé vypisovat, může vypadat takto:
X=
while true; do
X=${X}D
echo $X
done
Všimněte si složených závorek. Dovolíme si zde malé odbočení k proměnným.
Konstrukce ${X}
funguje stejně jako $X
, jen ji shell i v tomto
případě správně pozná. Jméno proměnné smí obsahovat písmena anglické abecedy,
číslice (kromě prvního znaku) a podtržítko. Shell čte tyto znaky za dolarem,
dokud se nezasekne o něco jiného, a teprve pak hledá, jestli proměnnou zná.
Kdybychom jako přiřazení použili X=$XD
, shell by neúspěšně hledal
proměnnou XD
, tedy by se do X
přiřazoval prázdný řetězec.
Mohli byste namítat, že stejně dobře a elegantněji bychom mohli přidávat místo
na konec na začátek pomocí X=D$X
a složeným závorkám se vyhnout. Máte
pravdu, tady to jde. Jenže ne vždy je možné se z průšvihu vylhat. Ještě můžeme
psát X=$X""D
, což je opravdu divné, a kdybychom už uvozovky používali,
musíme víc přemýšlet. Za chvíli bychom ještě byli rádi za složené závorky.
Cyklus, který jsme vyrobili, je nám zatím docela nanic, protože běží
donekonečna. Hodilo by se nám umět ho ve vhodnou chvíli zastavit, k tomu slouží
příkaz break
. Chová se podobně jako v jiných programovacích jazycích,
tedy ukončí provádění celého cyklu. Pokud ho spojíme se šikovnou podmínkou,
můžeme cyklus zastavit ve chvíli, kdy délka generovaného řetězce písmen D
překročí deset.
X=
while true; do
X=D$X
delka=`echo -n $X | wc -c`
if [ $delka -gt 10 ]; then break; fi
echo $X
done
Všimli jste si, jak je zjišťování délky proměnné neohrabané? Ještě si musí
člověk dávat pozor, jestli náhodou nepočítá i znak konce řádku… Taková
běžná operace, to přeci musí jít lépe! A taky že jo: podobně jako je ${X}
expandováno na obsah proměnné X
, tak je ${#X}
expandováno na její
délku.
Společně s příkazem break
jde ruku v ruce příkaz continue
, který
přeskočí všechny za ním následující příkazy, až před test podmínky další iterace
cyklu. Vynechání řetězců kratších pěti znaků se sice dá udělat i lépe, přesto
použijme příkaz continue
:
X=
while true; do
X=D$X
delka=${#X}
if [ $delka -lt 5 ]; then continue; fi
if [ $delka -gt 10 ]; then break; fi
echo $X
done
Až budete pracovat se zanořenými cykly a budete chtít příkazem break
nebo
continue
ovlivnit jiný než nejvnitřnější z nich, vzpomeňte si, že oba
příkazy mají nepovinný parametr. Zbytek už najdete. Pokud jste zvyklí na C,
příkaz goto byste hledali marně.
Jako třetí mezi podobnými přichází na řadu příkaz exit
, který najde
použití hlavně ve skriptech. Jeho zavolání ukončí shell (je tedy silnější než
break
), a když se mu předá číslo (třeba exit 42
), nastaví
ho jako návratovou hodnotu. S ní jsme se potkali ve druhém dílu. Ve zkratce
nula znamená úspěšné ukončení, cokoliv nenulového neúspěšné.
Návratová hodnota je na Linuxu 8-bitová (tedy může nabývat hodnoty 0–255), na
většině jiných UNIXových systémů je však jen 7-bitová (0–127). Bash sám o sobě
využívá hodnoty 126 a 127 pro své vlastní potřeby, tedy pokud chcete psát
univerzální programy, omezte se jen na hodnoty 0–125.
Pokud chcete zjistit, s jakou návratovou hodnotou skončil poslední provedený
příkaz, můžete k tomu využít speciální proměnnou $?
. Zkuste si třeba
zavolat příkazy true
, false
nebo (exit 42)
a hned po nich
echo $?
. Pokud exit
žádnou návratovou hodnotu nedostane jako
argument, použije právě hodnotu této proměnné.
Nacyklili jsme se dost, podívejme se ještě na složitější podmínky. Poprvé
potkáváme elif
. Funguje analogicky ke stejnojmenné konstrukci v Pythonu,
elsif
v Perlu, elseif
v PHP a prosté kombinaci else if
v C, Javě a Pascalu. Pokud na ni při vyhodnocování dojde řada, spustí
podmínku (Nezapomínejte, že podmínka je příkaz!) a kontroluje, jestli
uspěla. Pokud ano, nechá spustit kód za svým then
, jinak pokračuje další
větví podmínky (další elif
a jako poslední else
).
V košatějších podmínkách se může hodit nějakou větev mít, ale nic v ní nedělat.
Když hned za then
napíšete středník, shell si postěžuje na syntaktickou
chybu. Mohli byste použít příkaz, který nic nedělá, třeba true
, ale víc
se hodí funkčně shodná dvojtečka (:
). Její primární účel je
naznačení, že „tady nic není“, pro svou krátkost se ovšem zneužívá i na
místech, kam by se víc hodilo true
.
X=$(($RANDOM % 100))
if [ "$X" -lt 10 ]; then
echo malé
elif [ "$X" -le 42 ]; then
: nevím, něco mezi, mlčím
else
echo velké
fi
Zde je $RANDOM
speciální proměnná Bashe, která obsahuje při každé expanzi
nové náhodné číslo mezi 0 a 32767. Tento úryvek kódu tedy vygeneruje náhodné
číslo mezi 0 a 99 a rozhodne, jestli je malé, nebo velké. Pokud je vygenerované
číslo mezi 10 a 42, neumí se rozhodnout a mlčí.
Konstrukce $((...))
je aritmetická expanze. Shell výraz uvnitř vyhodnotí
a nahradí ji jeho hodnotou, přitom se k výrazu chová, jako by byl
ve dvojitých uvozovkách (expanduje proměnné, vyhodnotí zpětné apostrofy a výrazy
$(...)
). Pokud najde jméno proměnné (bez dolaru), přečte si její hodnotu.
POSIX vyžaduje, aby se v proměnných hledaly aspoň konstanty s volitelným
znaménkem, Bash jde ještě dál. Pokud obsah proměnné je možné interpretovat jako
výraz (třeba i jen jako jméno jiné proměnné), zkusí ho vyhodnotit. Teprve když
se mu to nepovede, vyhlásí chybu.
X=21+21
Y=X
Z=Y
echo $((Z))
echo $(($Z)) # funguje, jen zbytečně složitěji
Výrazy jsou jinak přejaté z jazyka C. Podporovány jsou desítkové, oktalové
a hexadecimální konstanty, většina aritmetických, bitových a logických
operátorů, závorky, podmínkový operátor (dvojice ?
a :
) a operátory
přiřazení. Bash podporuje i operátory ++
a --
. Vyhodnocuje se ve znaménkovém
celočíselném typu – norma vyžaduje signed long
nebo větší. Proběhlá
přiřazení do proměnných shell uvidí i po dokončení expanze.
Alternativou k shellové aritmetice jsou příkaz expr
, který podle nás
nemá žádné výhody, a příkaz bc
, který má vlastní jazyk a neomezenou
přesnost.
Funkce
V shellu už umíme kdeco z toho, co umí běžné programovací jazyky. Proměnné,
příkazy, řídicí konstrukce, aritmetiku, vstup a výstup, … O jednom
důležitém konceptu z programovacích jazyků ale dosud nepadlo slovo. O funkcích.
V běžných jazycích jsou funkce posloupností příkazů, která má nějaké (formální)
parametry. Při volání se funkci předají argumenty (skutečné parametry), které se
dosadí do formálních parametrů, a příkazy se spustí. V shellu je situace úplně
stejná.
Mnoho ze znalostí o skriptech, které jste si přinesli z předchozího dílu, platí
i pro funkce. Funkce také dostává poziční parametry, také má proměnné $#
,
$@
, $1
(až $9
), má návratovou hodnotu a volání funkce
i skriptu vypadají jako každý jiný příkaz (žádné závorky kolem argumentů!).
Na rozdíl od skriptu se pro funkci nespouští zvláštní shell, její příkazy běží
ve stejném procesu, který ji volá. Má tedy stejný obsah proměnné $0
, může využívat (a trvale měnit!) i neexportované proměnné a při zavolání exit
neskončí jen ona sama, ale i celý shell. Pro nastavení návratové hodnoty používá
příkaz return
.
Funkce se tedy chová stejně jako skript vložený příkazem .
(bashovsky
také source
), jen se jinak definuje a nemusí být ve vlastním souboru.
Ukažme si pro ilustraci definici funkce a její volání.
# definice
echo_t() {
test -t 1 || echo "$@" > /dev/tty
echo "$@"
}
# volání
echo_t hroch > soubor
Tato funkce se jmenuje echo_t
, všechny své argumenty vytiskne na svůj
standardní výstup, a pokud standardní výstup nejde na terminál (je přesměrovaný
a ne zrovna do terminálu), argumenty vytiskne i přímo na terminál.
Definice funkce začíná jejím jménem, následovaným kulatými závorkami. Pro jméno
funkce platí stejná pravidla jako pro jméno proměnné. Jmenné prostory funkcí
a proměnných jsou oddělené – můžeme mít stejně pojmenovanou funkci i proměnnou,
obě budou fungovat správně. Před jménem funkce Bash dovoluje ještě (zbytečné)
klíčové slovo function
.
Za jménem a kulatými závorkami následuje téměř obyčejný složený příkaz, jak ho
znáte z minula. Zvláštní je v tom, že místo aby se v něm hned expandovaly
proměnné a vůbec dělo všechno, co shell se svým vstupem provádí, shell si ho
zapamatuje beze změny a expanze provádí až při volání.
Za zmínku stojí, že za
složeným příkazem mohou být přesměrování vstupů a výstupů, která se stanou
součástí definice funkce; snadno tedy můžete napsat třeba hloupou funkci pro
záznam zpráv opatřených časem pořízení záznamu:
log() {
date
echo "$@"
echo
} >> zaznamy_chyb
Co kdybychom ale chtěli každý argument zalogovat jako samostatný záznam a na
každý záznam mít jeden řádek? Mohli bychom použít cyklus, který projde
jednotlivé argumenty. Proč bychom se ale nudili už dobře známým, když si můžeme
ukázat pěknou novou utilitu?
Úkol 5 [5b]
Napište funkci, která dostane jako svůj parametr jméno souboru se zdrojovým
kódem, načte a předzpracuje jej pro kompilátor a výsledek vypíše na standardní
výstup.
Zdrojový kód obsahuje jednotlivé textové tokeny oddělené právě jednou mezerou.
Komentáře jsou uvozené tokenem COMMENT
a platí do konce řádku. Program
je ukončený buď koncem souboru, nebo tokenem BYE
.
Úkolem funkce je opsat zdrojový kód až do konce programu s vynecháním komentářů.
Na výstupu by se neměly objevit ani žádné prázdné řádky.
Poznámka: Na tuto úlohu nepoužívejte příkazy grep
, sed
ani
žádný jiný jazyk (AWK, Perl, …).
Formátovaný výstup
Už jsme vám až příliš dlouho zamlčovali printf
. Mnoho jazyků má funkci
toho jména a ve všech má velmi podobný význam parametrů a stejný účel – pěkné
a snadné formátování výstupu. Na rozdíl od echo
implicitně neodřádkovává,
takže často budete na konec jejího prvního argumentu muset psát \n
. To je
další rozdíl proti běžnému echo
, umí céčkové escape-sekvence jako
\n
pro konec řádku nebo \t
pro tabulátor (echo
s přepínačem
-e
nebo POSIXové echo
escape-sekvence také vyhodnocují).
První argument printf
je šablona, do které dosazuje zbylé argumenty.
Pokud je argumentů málo, domyslí si pár prázdných navíc. Pokud jich je moc,
zopakuje šablonu. Toho využívá i výše slíbená vylepšená funkce log
:
log() {
d="$(date)" # znak % neobsahuje
printf "[$d] %s\n" "$@"
} >> zaznamy_chyb
V šabloně jsou formátovací direktivy, které poznáte podle znaku %
na
začátku. (Pokud procento potřebujete vypsat doslova, pište %%
.) Za každou
direktivu dosadí printf
pozicí odpovídající argument. Běžně budete
potřebovat %s
pro string a %d
pro desítkový zápis čísla. Mimo jiné
printf
umí i převádět čísla do šestnáctkové (%x
) a osmičkové
soustavy (%o
). Direktivy mají kromě povinného typu (s
pro
string, …) ještě dost nepovinných částí. Na jejich samostudium se dlouhé
zimní večery náramně hodí. ;-)
Úkol 6 [2b]
Napište skript, který formátuje /etc/passwd
do podoby tabulky.
Závěr
Tento díl byl delší než oba předchozí dohromady. Dotáhli jsme v něm ale do konce
spoustu rozdělaných věcí a poskytli vám tak mnohem ucelenější pohled na UNIXový
svět. Pokud bychom měli zrekapitulovat, co si z tohoto dílu máte odnést, mohl by
takový seznam vypadat následovně:
- Nápověda: vyhledávání pomocí
apropos
, sekce nápovědy, info
,
norma POSIX
- Souborové systémy:
df [-Th]
, du -h
, konvence na názvy souborů,
(ne)potřebnost přípon, file
- Typy souborů: inodes; běžné soubory, adresáře a zařízení reprezentovaná
soubory (
/dev/null
, /dev/tty
, …)
- Symlinky a hardlinky:
ln -s
a ln
, readlink
- Uživatelé a práva: význam skupin a práv pro soubory a adresáře,
su
a sudo
- Řídící struktury:
true
, false
a dvojtečka, continue
,
break
a exit
, vnořené cykly, expanze délky proměnné
(${#promenna}
), $RANDOM
a aritmetická expanze
- Funkce v shellu: definice, volání,
return
, souvislost se skripty
- Formátovaný výstup:
printf
V příštím díle se už konečně podíváme na slíbené utility pro práci s textem.
Přesným obsahem se nechte příjemně překvapit. :-)
Tomáš „Palec“ Maleček
Řešení