První série dvacátého sedmého ročníku KSP

Termín odeslání Vašich řešení této série jest určen na 20. října 2014 8:00. Termín odevzdání CodExové úlohy je pak 21. října 2014 8:00. Řešení můžete odevzdávat jak elektronicky, tak klasickou poštou na známou adresu.

Dotazy ohledně zadání můžete posílat na adresu ksp@mff.cuni.cz, nebo se ptát přímo na diskusním fóru KSP.

Zadání úloh

Tento rok jsme se rozhodli vám v každé sérii přinášet nějaký zajímavý příběh točící se okolo určité zajímavé programátorské chyby. Takové chyby dělá občas každý z nás, leč přesto někdy přerostou v příběh hodný zapamatování. V jednotlivých dílech příběhu se budeme pokoušet držet skutečných událostí, ale dovolíme si i jistou uměleckou licenci.

V prvním dílu se můžete začíst do příběhu, u kterého se nám nepodařilo zjistit, kde se přesně odehrál. Některé zdroje vsazují události tohoto příběhu do vojenského cvičení u Mrtvého moře a některé do Války v Zálivu (konflikt mezi Irákem a koalicí západních zemí v čele s USA v roce 1991), my jsme se rozhodli držet se verze z Perského zálivu.

Náš příběh začíná na palubě letadlové lodě USS Dwight D. Eisenhower přezdívané posádkou familiárně Ike

* * *

Poručík Stromboli rázoval chodbou k briefingové místnosti perutě. Ike byla dlouhá loď, na délku přes 300 metrů, a on dostal kajutu zrovna na opačném konci. Vyhnul se probíhající četě námořních pěšáků a během hlášení palubního rozhlasu o příletu zásobovacího letu se protáhl průchodem do briefingové místnosti.

Už od rána byla loď napjatá tím, kdy se konečně zapojí do probíhajících operací. Včera v noci vedly některé lodě operačního svazu masivní raketový útok na irácká radarová postavení a proslýchalo se, že dnes v noci se dostane řada i na ně.

A piloti už se nemohli dočkat, aspoň tak působil šum v místnosti, když Stromboli vešel a pokusil se sednout si na nějaké volné místo. Každý chtěl vidět co nejlépe na hlavní bojový plán, ale zároveň mezi piloty existoval určitý kodex, kterého se drželi i při uvolňování místa k sezení.


Teoretická úloha27-1-1 Zasedací pořádek (8 bodů)


Piloti v briefingové místnosti si postupně sedají na volné židle. Zasedací pořádek si můžeme představit tak, že máme nekonečně dlouhou řadu židlí (židlí je mnohem více, než dorazí pilotů) a každý nový pilot se nejdříve pokusí posadit na prostřední židli.

Pokud je židle, na kterou si chce pilot sednout, volná, je vše v pořádku, pilot se posadí a čeká na briefing. Pokud je však židle již obsazená jiným pilotem, tak, než aby se hádali, sedne si jeden z pilotů na židli o jedno místo vlevo a druhý na židli o jedno místo vpravo (původní židli tak nechají volnou).

Pokud by se při tomto rozsazování náhodou situace opakovala (pilot by se opět chtěl posadit na židli, kde už někdo sedí), bude se postup opakovat tak dlouho, dokud na každé židli nebude sedět maximálně jeden pilot.

Chceme po vás dvě věci:

  1. Jak bude řada židlí vypadat po příchodu N pilotů?
  2. Dostanete zapsaný nějaký zasedací pořádek pilotů (obsazené a neobsazené židle). Rozhodněte, zdali mohl vzniknout tímto postupem.

Řešení

Konečně se všichni posadili, do místnosti vešel velitel a začal briefing. Vypadá to dobře, dneska se konečně odlepí od letové paluby, zasnil se Stromboli, a tak si skoro nevšiml na něj mířené otázky.

„Tak Stromboli, přestaňte lelkovat a poslouchejte!“ napomenul ho velitel, „Říkal jsem, že dneska odpoledne provedete s Thompsonem průzkumný let v nízké výšce. Přesné pokyny a letový plán obdržíte během několika hodin, zatím se připravte. Technici vám zrovna na vašeho ptáčka montují průzkumnou výbavu.“

„Ano pane!“ odpověděl spěšně Stromboli a s úsměvem mrknul na svého navigátora Thompsona.

Schůze ještě chvíli pokračovala, než se rozdaly úkoly pro všechny piloty, a pak Stromboli v závěsu se svým navigátorem vyrazil směrem k hangárové palubě. Chtěl si ještě před akcí promluvit s vrchním zbrojmistrem a vybrat si vybavení.

Ve chvíli, kdy dorazil do zbrojnice, trochu se zděsil. Z vedlejšího zbrojního skladu se totiž ozýval děsivý lomoz, a tak tam opatrně nakoukl – vrchní zbrojmistr stál uprostřed místnosti a dirigoval sundavání palet se zbraněmi z vysokých polic.


Teoretická úloha27-1-2 Zbrojní sklad (9 bodů)


Kuchařková úlohaVrchní zbrojmistr na letadlové lodi potřebuje vyndat ze skladu několik palet s výzbrojí. Sklad je ale zaskládaný do veliké výšky a manévrování s neohrabanými vysokozdvižnými vozíky je v něm celkem nebezpečné, aspoň do doby, než se část věcí vyndá.

Ve skladu operují dva různé vysokozdvižné vozíky (určené pro palety dvou velikostí) a bezpečností předpisy dovolují na začátku sundavat pouze palety uložené na policích v maximální výšce h0. Na vstupu dostanete popis všech N palet ve skladu, paleta i je velikosti vi (velká nebo malá), je uložena ve výšce hi a má nebezpečnost xi.

Vozíky se musí v sundavání palet střídat (velká paleta, malá paleta, velká paleta, …) a po vyndání palety s nebezpečností xi mohou oba vozíky začít sundavat palety z výšky o xi větší než dosud.

Hlavního zbrojmistra by zajímalo, kolik palet může ze skladu vyvézt ven, aniž by porušil bezpečností předpisy.

Řešení

Konečně byla Stromboliho stíhačka připravená a vyzbrojená k průzkumné misi. Zašel si tedy na velmi pozdní oběd a pak se opět vydal do hangáru ke svému stroji. Usedl do kokpitu a pustil se do předletové přípravy. Po jejím dokončení pak ukázal palubnímu mechanikovi zdvižený palec a nechal se vyvézt výtahem na letovou palubu, kde počkal, než na něj dojde řada se startem.

Konečně, rameno katapultu se zakleslo za přední podvozkovou nohu „ef čtrnáctky“, Stromboli ukázal technikovi zdvižený palec, přidal tah motorů a pak už se jejich F14 Tomcat vyřítil po krátké vzletové dráze vstříc slunci.

Rychle vystoupali do výšky několika kilometrů a tam začali kroužit. Museli počkat, než dostanou od velitelství povolení k provedení akce. Thompson na zadním sedadle mezitím zapnul nový navigační systém, počkal, než se přijímač GPS ustálí, a začal prověřovat jeho funkčnost a propojení s průzkumným kontejnerem s kamerami, který měli zavěšený pod pravým křídlem.

Stromboli nechal Thompsona hrát si, navedl letadlo na kruhovou vyčkávací dráhu a čekal na finální pokyn k zahájení akce. Vzdušný prostor aktuálně brázdilo mnoho spojeneckých letounů – hlídky, zásobovací stroje i další průzkumné mise – a tak bylo potřeba udržovat přesně vymezené letecké koridory, aby se nikdo s nikým nesrazil. Naštěstí měla jejich mise nejvyšší prioritu.


Teoretická úloha27-1-3 Letecké koridory (10 bodů)


Vzdušný prostor nad spojeneckým námořním svazem není vůbec prázdný, a tak všechny letouny, které se v něm pohybují, musí dodržovat předepsané letové trasy neboli koridory.

Koridory mají předepsaný směr, kterým se jimi dá proletět, a vedou mezi určenými místy vzdušného prostoru (koridory a místa tak tvoří hrany a vrcholy orientovaného grafu).

Letoun se potřebuje dostat od letadlové lodi k místu plnění své mise, tedy je potřeba nalézt orientovanou cestu mezi dvěma zadanými místy.

Pilot letounu chce letět nejkratší trasou a zajímá ho, kolik má možností volby, tedy kolik různých nejkratších orientovaných cest vedoucích mezi těmito dvěma místy existuje (různé cesty jsou takové, které se liší alespoň v jedné hraně).

Řešení

Už začal přicházet soumrak, když konečně dostali očekávané rozkazy. Stromboli vysílačkou potvrdil příjem, uchopil knipl a začal s Tomcatem klesat. Když se přiblížili k pobřeží a sestoupili do několika desítek metrů nad vodu, snížil rychlost a změnil šípovitost křídel na pomalý let. Nastavitelná křídla, to byl důvod, proč tyhle starší stroje pořád miloval.

Letoun sestoupil ještě o kus níž, už letěli jen pár metrů nad vlnami. Pod nimi se mihla pláž, Stromboli navedl Tomcat do jedné prolákliny a vtom se to stalo!

Najednou za táhlého pískání zablikaly a zhasly všechny displeje v kokpitu a stroj se hrozivě otřásl, jak se začal naklánět na bok. Stromboli hned popadl knipl a přitáhl ho, šlo to mnohem hůř než obvykle. „Co se stalo, zasáhlo nás něco?“ křikl dozadu na Thompsona. „Nevím. Já…najednou všechno zhaslo, asi porucha.“

Stromboli zaklel, zatracená elektronika, pomyslel si. Jeho pohled zabloudil k panelu vysílačky u levého kolena, ta ještě jako jedna z mála svítila, nebyla připojená k modernizovanému palubnímu počítači. „Mayday, mayday. Tady Krysa jedna, volám základnu. Mayday, mayday. Těžká porucha palubní elektroniky, stroj stěží ovladatelný, vracíme se na základnu.“

Ještě že v zapadajícím slunci byla jasně vidět černá tečka letadlové lodi. Stromboli k ní zamířil a doufal, že to stroj zpět na loď zvládne, bez asistence palubní elektroniky totiž vůbec nevěděl, jaký je jeho stav. Thompson se mezitím vzadu pokoušel zprovoznit alespoň průhledový Head-up display, aby Stromboli při přiblížení viděl před sebou jejich rychlost.


Praktická opendata úloha27-1-4 Head-up display (10 bodů)


Head-up display (HUD) zobrazuje informace v zorném poli pilota, a ten tak nemusí sklánět oči dolů a pak se jimi zase vracet. Bohužel je však docela citlivý na vyladění barev a kontrastu, a pokud se nastaví nesprávně, spíš pilota ruší.

HUD má N různých prvků. Každému z nich můžeme nastavit kontrast na nějakou hodnotu mezi 0 a K včetně. Prvky jsou na HUDu uspořádány vedle sebe, takže si je můžeme představit jako řadu N čísel.

Když poprvé spustíme HUD, dostaneme nějak nastavený kontrast. Chceme přenastavit kontrast všech prvků tak, aby se žádné dva prvky vedle sebe nelišily o více než D jednotek, a zároveň chceme provést co nejmenší celkovou změnu kontrastu (součet změn bude nejmenší možný).

Formát vstupu: Na prvním řádku budou čísla N, K a D, na druhém řádku pak N čísel udávajících výchozí kontrast všech prvků. Čísla jsou na řádku oddělena mezerou.

Formát výstupu: Na první řádek vypište součet provedených změn, na druhý pak uveďte nové hodnoty kontrastu pro všechny prvky (tedy N čísel oddělených mezerou). Pokud existuje více optimálních řešení, vyberte si libovolné z nich.

Ukázkový vstup:
6 30 3
2 7 9 13 16 14
Ukázkový výstup:
3
4 7 10 13 16 14

Toto je praktická open-data úloha. V odevzdávacím systému si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.

Řešení

Stromboli navedl Tomcat na přistání a vysunul brzdící hák. Tohle přiblížení bez přístrojů nechtěl opakovat a byl rád, že slunce ještě nestihlo zapadnout. Už mu však moc nescházelo, a tak Stromboli s pozorností vybičovanou na maximum zahájil závěrečnou fázi přistávacího manévru.

Zadní kola dosedla na přistávací dráhu. Hák sice minul první brzdící lano, ale o druhé se již pevně zasekl a Tomcat, brzděný pružným lanem, zpomalil na několika metrech dráhy na nulu. Stromboli si vydechl, vypnul oba motory, sundal si helmu a prohrábl si zpocené vlasy. Tohle zvládli, teď bylo potřeba přijít na to, co se stalo.

Ještě ten večer si technici vzali jejich Tomcat do parády. Začali k němu připojovat všemožné diagnostické přístroje a zkoumali jejich údaje. Připojení diagnostických přístrojů k nefunkční elektronice ale není tak jednoduché, každý přístroj má totiž mírně odlišné požadavky na napájení.


Praktická CodExová úloha27-1-5 Napájení přístrojů (10 bodů)


Technici připojují diagnostické přístroje k rozbité elektronice. Každý kus elektroniky má nějaký svůj povolený rozsah napájení (minimální a maximální hodnotu napětí, při které bezpečně funguje).

Technici mají k dispozici laboratorní zdroje, které je možné nastavit na přesné napětí. Každý laboratorní zdroj může napájet neomezeně mnoho kusů elektroniky.

Protože jsou ale laboratorní zdroje hodně používaná věc a na technické palubě letadlové lodě je o ně velký zájem, chtějí jich technici použít co možná nejméně (aby jich co nejvíce zbylo na ostatní práce). Pomozte jim zjistit nejmenší možný počet zdrojů, se kterými ještě dokážou uspokojit požadavky napájení všech kusů elektroniky dohromady.

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í

Technici pracovali celou noc, ale na žádnou závadu na hardware nepřišli. Pro jistotu vyměnili jednotku palubního počítače a navigační systém. Další den večer se průzkumný let měl opakovat a rozkaz dostali opět Stromboli s Thompsonem.

Tentokrát se vznesli už za tmy a rovnou zamířili k oblasti, kterou měli v nízké výšce prolétnout. Stromboli opět klesl s letounem při nízké rychlosti do kaňonu a začali snímkovat kaňonem vedoucí silnici.

Pak Stromboli přitáhl Tomcat do těsného stoupání na konci kaňonu. Teď půjde do tuhého, pomyslel si, a navedl letoun nad hlavní cíl jejich průzkumu, těsný průlet nad iráckým letištěm. Díky letu v nízké výšce o nich do poslední chvíle nevěděli, a tak se protiletecká palba začala objevovat až s dlouhým zpožděním.

První zareagovala nějaká hlídka. Spustila palbu z ručních zbraní, se kterými ale neměli skoro žádnou šanci Tomcat zasáhnout. První protiletadlový kanón vystřelil až ve chvíli, kdy dokončili oblet letiště a začali se stáčet směrem zpět k základně.

Stromboli spustil přídavné spalování a za bojového pokřikování navedl letoun do táhlé zatáčky, která je dostala mimo dosah protiletecké palby. Rád by udržoval přídavné spalování déle a vychutnával si ten příval adrenalinu, když ho silné přetížení tlačilo do sedačky, ale pohled na ukazatel paliva mu to rozmluvil.

Stáhl výkon zpět do normálních hodnot a začal svým letem kopírovat zemi, aby se držel mimo dosah posledních fungujících iráckých radarů. Když v tom se to stalo znovu…

Tomcat právě klesal do nějaké prohlubně, když zhasly všechny displeje v kabině a přestal fungovat radar, kterým se Stromboli řídil při nízkém letu nad zemí. Instinktivně přitáhl letoun o kus výš, aby se vyhnul překážkám, které teď neviděl, a opět zahlásil do vysílačky celkové selhání palubní elektroniky.

Teď byla ale situace vážnější, byla noc a návrat na základnu byl o to těžší. Z letecké patroly v blízkosti byla odvelena jedna F15, která je rychle dohnala a s rozsvícenými pozičními světly se usadila půl kilometru před nimi. Takhle je jako pasáček ztracenou ovci dovedla až nazpět k Ike, kde mezitím palubní personál řešil další problém s navigačními světly.


Teoretická úloha27-1-6 Přistávací světla (12 bodů)


Navigační světla na palubě letadlové lodě jsou tvořena třemi barvami: červenou, zelenou a modrou. Jsou rozmístěna podél dráhy v řadě N světel.

Palubní důstojník chce rozsvítit nějaký úsek světel tak, aby při pohledu skrz všechna svítící světla působil bíle. A zároveň chce, aby světlo bylo co nejsilnější, tedy aby svítilo co možná nejvíce světel.

Potřebuje tedy najít nejdelší souvislý úsek, ve kterém jsou všechny tři barvy zastoupeny ve stejném počtu.

Lehčí variantaLehčí varianta (za 8 bodů): Vyřešte stejnou úlohu, ale jen pro dvě různé barvy.

Řešení

Díky dobře osvětlené přistávací dráze a skvělému pilotnímu umu se Strombolimu povedlo i podruhé usadit letoun do brzdících lan bez jakékoliv navigační pomoci. Teď už byl však rozhněvaný, dvakrát stejná závada se mu vůbec nelíbila. Během převozu letadla výtahem na hangárovou palubu tedy prohodil několik nevybíravých vět s vrchním mechanikem a po ohlášení u velitele a krátkém hlášení padl vyčerpaný do postele. Alespoň že snímky tentokrát dovezli, a mise tak byla hotová.

* * *

Technici mezitím znovu prolezli celou F14 a hledali závadu v hardware. Nikde však žádnou nenašli, a tak obrátili svůj pohled k softwaru. A zde je čekalo velké překvapení, jedno drobné přehlédnutí, které způsobilo pád celého palubního počítače.

Letoun byl totiž vybaven novou verzí systému GPS, která mimo jiné počítá podle signálu z družic i nadmořskou výšku. Ukázalo se však, že se v jednom místě nadmořskou výškou dělí, aniž by byla zkontrolována nenulovost této hodnoty. A jelikož poručík Stromboli navedl letoun při obou letech do nízkého průletu kaňonem, jehož nejnižší bod se nacházel pod úrovní referenční mořské hladiny používané v GPS, došlo v obou případech k dělení nulou.

To pak vlivem propojení přístrojů v F14 zapříčinilo pád zbytku elektroniky (řízení ale ovlivněno nebylo, to je v F14 přenášeno ještě hydraulicky a mechanicky). Technici se z tohoto problému snad poučili a aktualizovali software zbytku amerických letadel – alespoň od té doby žádné podobné příběhy nejsou. Nebo vlastně… ale o tom zase třeba příště.

Jirka Setnička


Seriálová úloha27-1-7 Učíme se s UNIXem (14 bodů)


V letošním seriálu jsme se rozhodli trochu vás seznámit s UNIXovými systémy, přesněji hlavně s tím, jak efektivně (a efektně) používat jejich příkazovou řádku. Naučíme vás, že UNIXová příkazová řádka je kamarád, kterého se nemusíte bát – neoplývá sice (většinou) klikacím barevným rozhraním, kterým vás provede virtuální pomocník v podobě pana Sponky (personifikovaná nápověda v jednom nejmenovaném kancelářském balíku), ale o to je mocnější.

Seriál bude směřován hlavně praktickým způsobem a jednotlivé úkoly by vás měly naučit UNIX skutečně používat. Nejprve se ale nevyhneme malému historickému úvodu o vývoji UNIXu a různých shellů, hlavně bashe.

UNIX, POSIX, shell, bash, … Co to všechno je?

Historie UNIXu se začala psát v roce 1969, kdy v Bellových laboratořích vznikl tak trochu potají nový operační systém (vedení firmy v nově vyvíjeném operačním systému tehdy nevidělo velkou budoucnost, a tak ho jeho vývojáři před vedením vydávali za textový editor, aby na vývoj získali čas a zdroje). Teprve začátkem 70. let dostal systém oficiální podporu a začal se překotně vyvíjet.

UNIX je ale v současnosti registrovaná ochranná známka a mohou ji využívat pouze systémy, které splňují určené podmínky a k tomu platí licenční poplatky (navíc licenci získává vždy jen určitá verze systému, a licencování je tak pro rychle se vyvíjející systémy finančně i časově neúnosné).

Z tohoto důvodu vzniklo několik systémů, které jsou od UNIXu pouze odvozené. Všechny ale splňují společný standard zvaný POSIX, který zaručuje vzájemnou kompatibilitu, a obecně se o nich mluví jako o UNIXových systémech. Nejrozšířenějším z nich je Linux, který sám existuje v záplavě různých variant (tzv. distribucí). Dalším známým systémem je například BSD vyvíjený na Kalifornské univerzitě v Berkeley.

Všechny tyto systémy ale spojuje příkazová řádka, ve světě UNIXu se jí říká shell a běží v terminálu. Terminál je přímo věc, která se stará o čtení vstupu z klávesnice a zobrazení výstupu na monitor, ale nemá žádnou vnitřní logiku, o tu se stará shell (který se zase nemusí zajímat o klávesnici a monitor, ale má už jen svůj standardní vstup a výstup).

Shell je jednoduše řečeno textové rozhraní, které umožňuje spouštět příkazy a pomocí nich ovládat celý systém. Je to takový předchůdce grafických rozhraní a existují stroje (například některé servery), kde grafické rozhraní vůbec není nainstalováno a celé ovládání se děje právě jen přes shell.

Ale i shell sám existuje v několika různých variantách, i on se postupně vyvíjel a byl obohacován o nové vlastnosti a příkazy. Shell, se kterým se dneska setkáme skoro na všech linuxových strojích, se nazývá bash (zkratka za Bourne again shell, což je odkaz na starší Bourne shell). V něm se budeme pohybovat většinu času, ale pokusíme se zdůrazňovat, které příkazy jsou univerzální a budou fungovat ve všech POSIXových shellech, a které jsou jen specialitou bashe.

Jak si bash pořídit?

Pokud máte Linux, skoro určitě máte bash nainstalovaný. Stačí ve vašem systému pouze spustit Terminál (či nějak podobně nazvanou aplikaci) a objeví se vám (většinou černé) okno, kam je možné psát příkazy a prohlížet si jejich výsledky. Je ale možné, že na svém stroji nebudete mít nastavený bash jako výchozí a spustí se vám nějaký jiný, jednodušší, shell. V takovém případě v něm jen spusťte příkaz exec bash a jste v bashi.

Ve Windows je situace o trochu složitější, ale i zde si můžete UNIXový bash pořídit (organizátoři KSP ho při práci ve Windows doporučují jako věc, která vám usnadní život). Nejlepším řešením bude instalace programu Cygwin, který vám nainstaluje bash a spoustu šikovných utilit. Jeho instalaci máme popsanou v naší Encyklopedii.

První kroky po systému

Než vůbec uděláme první krok, měli bychom si v rychlosti představit UNIXový souborový systém. Jeho hlavní myšlenkou je, že vše je uspořádáno ve stromu, do kterého se na různá místa zapojují správné věci, třeba různé disky. Kořen souborového systému se označuje jako adresář / (lomítko), domovským adresářem uživatele hroch pak adresář /home/hroch. Je klidně možné, že celý adresář /home sídlí fyzicky na jiném disku (třeba i síťovém), který se do stromu souborů připojil na správné místo.

Názvy souborů i adresářů mohou tvořit libovolné znaky (mezery, písmena s diakritikou, …) s jedinou výjimkou, a tou je lomítko, to se totiž používá pro oddělování názvů adresářů v cestě. Nedoporučujeme ale vytváření názvů obsahujících různé speciální znaky jako []"?* a podobně (může se dokonce stát, že některé programy a systémy budou – pro vaši ochranu – vytváření takových souborů blokovat).

V názvech také záleží (na rozdíl od operačních systémů Windows) na velikosti písmen, fotka.jpgfotka.JPG jsou rozdílné soubory.

Když poprvé spustíme bash, uvidíme podobný řádek:

hroch@ksp:~$

Ten nám říká náš login (jméno, pod kterým jsme se přihlásili), stroj, na kterém náš bash běží, a také nám prozrazuje jméno adresáře, ve kterém se aktuálně nacházíme. Za znakem $ pak můžeme psát příkazy. Ale počkat, co je vlnka za adresář a kde je připojený ve stromu souborů? Není to nic tajuplného, je to jen zkratka za náš domovský adresář, tedy za /home/hroch, aby ve výpisu nezabíral tolik místa.

Začneme se shellovým Hello World: Napište příkaz echo Hello World a spusťte ho klávesou Enter. Příkaz echo (anglicky „ozvěna“) udělá to, že opíše všechny své parametry na svůj výstup.

Většina příkazů v shellu totiž může být ovlivněna zadanými parametry, některé příkazy bez zadaných parametrů dokonce ani nefungují. Parametry jsou od sebe a od příkazu v shellu odděleny mezerami a rozlišujeme dva základní typy parametrů – přepínače a poziční argumenty. K jejich pořádnému vysvětlení se dostaneme za chvíli, zatím si pojďme ještě chvíli hrát.

Chcete nějaký zajímavější příkaz než echo? Zkuste si spustit příkaz pwd. Tento příkaz vypíše cestu do aktuálního adresáře (je to zkratka za „print working directory“). Jak se ale přepnout do nějakého jiného? K tomu slouží příkaz na změnu adresáře cd („change directory“), tomu do parametru můžeme napsat, kam nás má přepnout (bez parametru nás přepne do našeho domovského adresáře).

Zkuste si třeba cd / nebo cd /home. Cestám zapsaným s lomítkem na začátku říkáme absolutní a udávají přesné místo v adresářovém stromě, kam se přesunout. Druhou možností je použít cestu relativní vzhledem k pracovnímu adresáři, ta se píše bez úvodního lomítka. Například spuštění cd hroch v adresáři /home nás přesune do /home/hroch. Můžeme si to představit tak, že před takovouto cestu se automaticky připojí výstup příkazu pwd a lomítko.

Speciálním „podadresářem“, který se vyskytuje všude, je adresář .. (dvě tečky). To je odkaz ukazující o úroveň výš, když tedy ve svém domovském adresáři spustíme cd .., přepne nás to do adresáře /home. Dalším speciálním adresářem je adresář . (tečka), která odkazuje na aktuální adresář – to vám teď může připadat matoucí a nadbytečné, ale v dalších dílech ukážeme, na co se tento odkaz používá.

Poslední, co k pohybu po systému potřebujeme, je příkaz, který by nám vypsal obsah aktuálního adresáře. To je příkaz ls (od anglického slovesa „list“). V základní verzi nám vypíše všechny adresáře a soubory v aktuálním adresáři kromě skrytých (začínajících tečkou), později se s ním naučíme některé další šikovné věci.

Pokud si budete chtít nějaký ze zmíněných příkazů více prostudovat, můžete použít manuálové stránky. Pro zobrazení manuálových stránek k příkazu abc zadejte v bashi příkaz man abc, a pokud k příkazu abc tato stránka existuje, zobrazí se vám (k příkazům cd a pwd ale v některých systémech manuálové stránky neexistují, jelikož se jedná o interní příkazy shellu a ne o samostatné programy).

V manuálové stránce je většinou uvedený základní popis příkazu, možné parametry a občas i ukázkové použití. Pro ukončení prohlížení a návrat do shellu stiskněte q. Doporučujeme si zběžně pročíst manuálové stránky dále zmiňovaných příkazů, mohou se vám hodit.

Vytváření a mazání adresářů a souborů

Již umíme procházet po adresářích, pojďme si také nějaké vytvořit a smazat:

  • Vytvoření prázdného adresáře provedeme zavoláním příkazu mkdir název adresáře.
  • Smazání adresáře (musí být prázdný) uděláme pomocí příkazu rmdir název adresáře, nesmíme se v tu chvíli ale nacházet uvnitř tohoto adresáře.
  • Vytvoření prázdného souboru můžeme provést pomocí příkazu touch název souboru. Pokud takový soubor neexistuje, příkaz ho vytvoří; pokud existuje, nastaví mu datum modifikace na aktuální okamžik.
  • Smazání souboru zařídíme zavoláním rm název souboru.

Všechny názvy můžeme uvádět i jako cesty, zavolání touch adresar/soubor vytvoří souboradresar, pokud adresar již existuje (jinak skončí zavolání chybou). Pokud chceme najednou spustit více příkazů, dá se to provést jejich zápisem na jednu řádku a oddělením pomocí středníku.

Úkol 1 [2b]

Vytvořte prázdný soubor test umístěný v nově vytvořeném podadresáři ~/a/b/c/d (vlnka tu, jak je zvykem, zastupuje váš domovský adresář). Pak zase adresáře i soubor vymažte. Zkuste použít co možná nejméně příkazů.

U všech úloh odevzdávejte posloupnost příkazů vedoucích ke splnění dané úlohy (pokud nebude uvedeno jinak).

Ještě doplníme další tři příkazy, které souvisejí s prací se soubory:

  • Příkaz cp (zkratka za copy) slouží ke zkopírování (klidně více) souborů do zadaného umístění. Původní soubory zůstanou na místě a v cílovém umístění se vytvoří jejich kopie.
  • Příkaz mv (zkratka za move) dělá podobnou věc jako cp, jen soubory nekopíruje, ale přesouvá (a umí přesouvat i adresáře). Stojí za poznámku, že pokud je původní i cílové umístění na stejném fyzickém disku, je mv řádově rychlejší, než kombinace cprm – jen se totiž upraví záznam v tabulce souborů a data se fyzicky nemusejí nikam přesouvat (dalo by se říci, že vlastně dojde jen k přejmenování a přepsání adresy).
  • Příkaz cat (zkratka concatenate) vypisuje obsah zadaných souborů na terminál. Hodí se jednak pro prohlížení obsahu krátkých souborů, jednak pro svůj původní účel (konkatenaci – zřetězení). Když zadáme více názvů souborů, cat je všechny spojí a vypíše na terminál (později se dozvíme, jak výstup na terminál přesměrovat někam, kde se nám hodí víc).

Příkazy cpmv přebírají libovolně mnoho parametrů: vezmou všechny parametry až na poslední jako zdrojové soubory a zkopírují/přesunou je do místa, kam odkazuje poslední parametr. Teď by stálo za to pořádně si rozebrat, co všechno může být obsaženo v parametrech a jaké triky s nimi umíme.

Parametry: Přepínače a poziční argumenty

Přepínače jsou, jak již název napovídá, parametry, které upravují nějakým způsobem běh příkazu. Jsou uvozeny jednou nebo dvěma pomlčkami (je zvykem, že jednou pomlčkou jsou uvozeny jednopísmenné a dvěma pomlčkami vícepísmenné, ale neplatí to vždy).

Například nám již známý příkaz ls má přepínač -l zapínající dlouhý výstup. Když si tedy spustíme příkaz ls -l, vypadne na vás pravděpodobně výpis podobný tomuto:

drwxr-xr-x  hroch ksp 4096 čen 16 12:00 adresar
-rw-r--r--  hroch ksp    0 čen 16 12:00 soubor
Zde se dozvíme (popořadě) přístupová práva k souboru, jeho vlastníka a skupinu (těmito věcmi se budeme zabývat v některém z příštích dílů), velikost, datum poslední změny a na úplném konci nalezneme název.

Při zápisu více přepínačů je můžeme psát buď všechny samostatně (prikaz -a -b -c), nebo můžeme jednopísmenné i sdružit dohromady (prikaz -abc), fungovat budou stejně. Pokud nějaký přepínač bude přijímat doprovodný parametr (většinou to je číslo), může zápis vypadat třeba takto: prikaz -ab 3 -c (tady přepínač b přijal parametr 3), nebo dokonce prikaz -acb3.

Pořadí přepínačů je u většiny základních příkazů libovolné (když nebude, upozorníme vás), ale u některých programů, které nejsou napsané tak pečlivě jako základní shellové příkazy, na jejich pořadí záležet už může. V takovém případě doporučujeme pročíst manuál od daného programu.

Druhý typ parametrů nazýváme poziční argumenty. Ty se zadávají bez nějakých uvozujících pomlček a tradičně až za všemi přepínači. Často to jsou například názvy souborů a adresářů (viz příkazy cpmv).

Zvídavější z vás možná ve spojitosti s výše jmenovanými příkazy napadla jedna otázka: „Jak zkopírovat/smazat soubor s mezerou v názvu?“ Na to se dá jít dvěma způsoby:

  • Speciální znaky (mezi něž patří i mezera) můžeme escapovat, tedy zbavit je jejich speciálního účinku, předřazením zpětného lomítka. Napsáním rm deravy\ nazev tedy předáme příkazu rm jediný parametr – název souboru obsahujícího mezeru.
  • Druhou možností je použití uvozovek, příkaz výše bychom mohli přepsat na rm "deravy nazev" se stejným účinkem. Použít se dá i zápis s jednoduchými uvozovkami (apostrofy) a v tomto případě by byly ekvivalentní, rozdíl je však v tom, že ve dvojitých se expandují proměnné, kdežto v jednoduchých ne (více o proměnných v příštích dílech).

Poslední důležitou věcí ohledně parametrů je, jak oddělit přepínače od pozičních argumentů. Představme si například, že bychom chtěli smazat soubor, který by se jmenoval „-f“. Nemůžeme napsat jenom rm -f, protože to se vyhodnotí jako přepínač, a ani rm "-f" nám nepomůže, protože bash stejně předá příkazu rm jenom parametr -f.

Řešením je oznámit příkazu místo, kde končí přepínače. To uděláme pomocí osamocené dvojice pomlček. Za tímto místem se již nemohou nacházet žádné přepínače a příkaz všechno zbylé vyhodnotí jako poziční argumenty. Řešení tedy vypadá takto: rm – -f.

Úkol 2 [2b]

Prostudujte si manuálové stránky příkazů headtail, hlavně jejich parametry, a zjistěte, jak vypsat prvních a posledních patnáct řádků souboru a jak vypsat všechno až na prvních patnáct řádek. Vyzkoušejte si to třeba na souboru /etc/passwd.

Doplňování a wildcardy

Představme si, že se chceme přepnout do adresáře, jenž má hrozně dlouhý název. Bash nám to dokáže usnadnit: Pokud totiž při psaní názvu zmáčkneme tabulátor, pokusí se doplnit (podle již napsaného začátku) zbytek názvu souboru nebo adresáře.

Pokud existuje několik souborů nebo adresářů, které mají stejný prefix jména, doplní bash po stisku tabulátoru nejdelší společnou část a po dalším stisku zobrazí jména, kterými se dá pokračovat. Pak stačí jen napsat další část názvu, opět stisknout tabulátor a nechat si doplnit zbytek. Věřte, že to řádově urychlí pohyb po adresářovém stromě a je to jedna z nejpoužívanějších kláves. :-)

Dalším dobrým trikem jsou šipky nahoru a dolů, které nám dovolí listovat v historii příkazů a znovu je spouštět nebo upravovat.

Dobře, teď již umíme s bashem pracovat efektivněji, ale co když budeme chtít zkopírovat stovky souborů (třeba fotek z výletu), to je musíme vážně všechny vypisovat? Bash nám pomůže i v tomto případě, užitím zástupných značek neboli wildcardů.

Wildcardy fungují jako žolíky, umožní nám nahradit část názvu souboru zástupným znakem. Ve skutečnosti se stane to, že bash najde všechny soubory, které odpovídají použitým zástupným znakům, a nahradí jimi výraz s wildcardy v příkazu (říkáme, že se expanduje na tyto soubory). Samotný příkaz tedy nevidí wildcardy, ale dostane od bashe rovnou seznam odpovídajících souborů. Jedinou výjimkou je, když žádné takové soubory neexistují, v takovém případě nechá bash výraz s wildcardy beze změny.

Mezi wildcardy patří:

  • Otazník ? zastupuje libovolný znak: mal?.txt tedy odpovídají například soubory mala.txt, maly.txtmale.txt (naopak mal.txt neodpovídá).
  • Hvězdička * zastupuje libovolný (i nulový) počet nějakých znaků: fot*.jpg odpovídají foto001.jpgfot.jpg. Speciální výjimkou jsou skryté soubory, na ty se wildcardy neexpandují (pokud explicitně nenapíšeme tečku na začátku názvu).
  • Hranaté závorky [] se používají pro výčet nebo rozsah: výrazu [13579] bude odpovídat libovolná lichá číslice, výrazu [0-9] libovolná číslice z rozsahu 0 až 9 a výrazu [0-9A-F] zase libovolná šestnáctková číslice. Pokud jako první znak v závorce uvedeme stříšku, funguje celá závorka jako negace (výrazu [^0-5] odpovídá všechno až na číslice 0 až 5). Stejného efektu docílíme ve většině moderních shellů také vykřičníkem.

Další speciální konstrukcí shellu, která se často kombinuje s wildcardy, jsou složené závorky {}. Nejsou to wildcardy, takže se vůbec nedívají na to, jestli nějaké jimi popisované soubory existují nebo ne, ale daly by se přirovnat spíše k syntaktické zkratce.

Jejich zápis je tvořen několika výrazy oddělenými čárkami (ve spojení s wildcardy například *.{jpg,mp[34]}) a bash udělá to, že ještě před zpracováním klasických wildcardů rozepíše výrazy obsahující složené závorky na všechny jejich možné varianty – vytvoří samostatný výraz pro každou z variant uvedených ve složené závorce (z příkladu výše tak vznikne dvojice *.jpg *.mp[34], která se teprve zpracovává dál).

Pokud budeme pracovat v bashi (jiné shelly podobnou funkci obecně mít nemusí, i když zase mohou obsahovat jiná vylepšení), můžeme ve složených závorkách použít i rozsah. Zápis {1..20} je ekvivalentní s vypsáním dvaceti čísel oddělených čárkou ve složených závorkách.

Rozdíl oproti wildcardům se dá pozorovat třeba mezi příkazy mkdir adresar{5,6,7}mkdir adresar[567]. První provede, co bychom od něj očekávali (vzniknou tři nové adresáře), ale druhý pro neexistenci daných adresářů vytvoří adresář s hranatými závorkami v názvu (wildcardy se neexpandují).

Pro zkoušení wildcardů v nějakém adresáři můžete použít příkaz echo, který vám vypíše všechno, co dostane jako parametry – tedy všechny expandované názvy souborů v aktuálním adresáři, nebo původní wildcard, pokud se expanze nepovedla.

Úkol 3 [1b]

Jak byste smazali soubor, který obsahuje v názvu nějaký wildcard? Například jak byste smazali soubor a?c a přitom nesmazali abc, nebo smazali adresar[567], ale již ne existující adresar5?

Úkol 4 [3b]

Vymyslete co nejkratší zápis pomocí wildcardů, kterému budou odpovídat právě všechny soubory s příponami jpg, jpeg nebo gif v podadresářích aktuálního adresáře; názvy podadresářů musí obsahovat buď alespoň dvě číslice 0 až 9 nebo alespoň dvě písmena anglické abecedy (pozor na velikost písmen). Důkladně popište, co která část výrazu dělá. Můžete předpokládat, že od každého typu bude alespoň jeden soubor a adresář existovat.

Roury a přesměrování vstupu a výstupu

Kromě parametrů pracují ještě shellové příkazy se vstupem a výstupem. Parametry většinou slouží k nastavení chování příkazu, kdežto data, která chceme příkazem zpracovat, patří na jeho vstup.

První metodou zadávání vstupu je přímo jeho psaní na terminál. Zkuste si spustit například příkaz wc (zkratka za word count). Ten slouží k počítání slov, písmen a řádek souborů, které dostane jako parametry, což ale teď nebudeme používat – bez pozičních argumentů totiž wc provádí to samé se svým standardním vstupem a na svůj standardní výstup vypisuje výsledek. Ve výchozím nastavení směřují standardní vstup i výstup na terminál.

Když wc spustíme, můžeme psát libovolný text, Zadávání ukončíme speciálním znakem EOF (End-of-file), který napíšeme stiskem Ctrl+D na prázdném řádku. Tím ukončíme vstup a wc provede svoji práci – vypíše dané počty. Pokud bychom chtěli příkaz ukončit bez toho, aby vypsal výsledek, můžeme ho násilně zastavit pomocí Ctrl+C.

Toto ale není příliš praktické použití, mnohem lepší by bylo přesměrovat na vstup nějaký soubor. To uděláme pomocí operátoru šipky: Když zapíšeme wc < soubor.txt, tak přesměrujeme obsah zadaného souboru na standardní vstup příkazu wc. Zkuste si to. Stejně tak můžeme zápis převrátit a jako první napsat přesměrování. Dokonce nemusíme ani okolo operátoru přesměrování psát žádné mezery (lze tedy psát <soubor.txt wc). Syntaxe je dost volná, stačí si zvolit styl, který se vám bude nejvíce líbit.

Stejně jako vstup můžeme přesměrovat i výstup, to uděláme šipkou ukazující na druhou stranu, směrem k souboru. Příkaz ls > seznam.txt přepíše soubor seznam.txt a uloží do něj výstup z příkazu ls, na terminálu se v takovém případě žádný výstup neobjeví. Kdybychom namísto přepsání souboru chtěli jenom připojit nové řádky na jeho konec, můžeme použít dvojitou šipku: ls >> seznam.txt.

Co když budeme chtít použít výstup jednoho příkazu jako vstup pro druhý? Určitě bychom mohli použít pomocný soubor, třeba pomocí a > tempfile ; b < tempfile, ale shell nám nabízí mnohem elegantnější věc, a tou je takzvaná roura.

Roura se zapisuje jako svislá čára (na anglické klávesnici ji najdeme nad Enterem) a funguje tak, že vezme standardní výstup příkazu nalevo od sebe a použije ho jako vstup pro příkaz napravo. Zápis a < soubor | b | c (nebo ekvivalentní <soubor a|b|c) znamená to, že spustíme příkaz a, kterému předáme na vstupu soubor a jeho výstup použijeme jako vstup pro příkaz b. Výstup příkazu b pak použijeme jako vstup pro c a výstup c zobrazíme na terminálu. Jednotlivé programy přitom běží současně a mezivýsledky si rovnou předávají, ty tedy nezabírají žádné místo na disku.

Pokud budete používat přesměrování do souboru, dejte si ale pozor na jednu věc. Nelze najednou načítat a zapisovat stejný soubor, shell totiž jako první smaže původní soubor (pokud používáme přesměrování jednou šipkou), a pak teprve by se ho pokoušel načíst. Pozor na to hlavně při úpravách již hotového textu, dá se takto nenávratně smazat několikahodinová práce.

Úkol 5 [1b]

Do souboru datum vypište na první řádek „Dnes je:“ a na druhý aktuální datum a čas v jakémkoliv formátu. Asi vám k tomu pomůže příkaz date a pro zkontrolování obsahu souboru můžete použít buď již zmíněný cat, nebo prohlížeč souborů less – ten se ukončuje stiskem q.

Úkol 6 [1b]

Spočtěte nějakým příkazem počet adresářů a souborů ve svém domovském adresáři (kromě skrytých).

Úkol 7 [2b]

Napište příkaz využívající roury, který ze souboru soubor.txt vezme jedenáctou až třicátou řádku včetně a spočítá počet slov na nich. Můžete předpokládat, že soubor.txt je dostatečně dlouhý.

Úkol 8 [2b]

Vypište velikost v bajtech všech souborů v aktuálním adresáři, které obsahují v názvu alespoň jednu číslici (nezapomeňte na skryté soubory).

Závěr

Dnes jsme si prošli základní příkazy a principy použitelné v bashi. Zopakujme si všechny příkazy, které jsme se naučili:

  • Navigace: pwd, cd, ls
  • Manipulace se soubory: touch, cp, mv, rm, mkdir, rmdir
  • Obsah souborů: cat, head, tail, wc
  • Další: echo, less, date
  • Manuál: man
  • Wildcardy, roury a přesměrování vstupu a výstupu.

Příští díl se již můžete těšit na některé pokročilejší UNIXové techniky, my se budeme těšit na vaši účast. :-)

Jirka Setnička

Řešení