(Učební text pro předmět "Úvod do Unixu")
Následující traktát budiž uvolněn pod licencí GPL. Jako podklady pro jeho vypracování byla použita zejména má několikaletá zkušenost s operačním systémem Linux.
Hlavní cílovou skupinou jsou moji studenti extrémisti, ale budu se snažit psát dostatečně obecně, aby text mohli využívat i jiní zájemci. Neručím za to, že všechno je správně či podle norem (nebudeme si nic nalhávat, z operačních systémů unixového typu znám důvěrně pouze Linux), nicméně můžete si být jisti alespoň tím, že předkládaná fakta nejsou ve sporu s mými požadavky na zápočet :) Pokud vám něco nebude jasné nebo mi tam dokonce najdete chybu :), nebojte se mě kontaktovat na mailu johanka@ucw.cz
(nepovinné, ale vhodné :))
Základním elementem operačního systému, který nás bude zajímat, je terminál, konzole, příkazová řádka, či jakkoli jinak tomu chcete říkat (každý ten termín znamená něco trochu jiného, ale to teď není podstatné). Obecný Unix může mít textovou konzoli (console), virtuální terminály (tty), z nichž na některých může běžet gettty (tato kombinace je ekvivalentní textové konzoli) a na některých gratický režim (X Window System, dále jen Xka), většinou má nějakou podmnožinu z toho.
Do ruky se vám dostane nejspíš Linux, a ten má 12 fyzicky dostupných virtuálních terminálů (tty1 - tty12) (obecně jich má podstatně víc, ale zřídka se používají nějaké jiné než prvních 12, protoze těchto 12 je snadno dosažitelných klávesovými zkratkami Alt+F1 - Alt+F12, resp. Ctrl+Alt+Fx pokud přepínáme z Xek). Výchozí konfigurace je, že na prvních šesti (tty1 - tty6) běží gettty, tedy chovají se jako konzole (užívejme tedy dále označení "konzole", ačkoli to není přesné) a vidíte na nich černou obrazovku a nápis login:, a na sedmém běží Xka, běží-li. Pokud výchozí nastavení počítače spouští více instancí gettty, např. až na tty9, obsadí Xka nejbližší volné tty.
Xka se buď spouští při startu, v tom případě mají také přihlašovací obrazovku a může se do nich přihlásit kdokoli, nebo si je může uživatel po nalogování se do konzole spustit sám příkazem startx, v tom případě běží od začátku pod tímto uživatelem. Instancí Xek může běžet několik, například každý uživatel může mít spuštěnou jednu, v tom případě každá z těchto instancí obsadí jedno (nejbližší volné) tty.
Na neobvyklých architekturách s komerčními Unixy (Sun se Solarisem, SGI s IRIXem apod.) nenaleznete vůbec fyzicky dostupné virtuální terminály, tyto počítače většinou mají jen jednu konzoli (tentokrát míněno v původním významu), na které je buď textový, nebo grafický režim. Veškerou činnost, na kterou potřebujete příkazovou řádku, provádíte v Xkách na emulátorech terminálu (xterm apod.), to se samozřejmě týka i Linuxu, pokud tam v nich pracujete.
Jak nalézt v Xkách emulátor terminálu, teď není podstatné, nyní si povíme, jak se přihlásit. Na počítači musíme mít účet, potom známe své uživatelské jméno a heslo. Stejným jménem a heslem se přihlašujeme jak na všechny textové konzole, tak do Xek (jsou-li spuštěna při startu) a také ze vzdáleného počítače (viz dále). Jako login uvedeme své jméno, jako heslo heslo, a pokud jsme to nezvorali, jsme přihlášeni :). Pokud si chceme heslo změnit (což často při prvním přihlášení chceme), napíšeme příkaz passwd, ten se nás zeptá na staré heslo, dvakrát na nové, no a případně může kafrat, že nové heslo je příliš kráké, jednoduché, podobné nějakému slovu apod., takže si musíme vymyslet nějaké lepší.
Pokud jsme se přihlásili na konzoli, máme příkazovou řádku (která nás zajímá) hned, pokud jsme se přihlásili do Xek, musíme si najít a spustit emulaci terminálu. To, kde ji najdeme a jak se jmenuje, záleží na použitém operačním systému i Window Manageru, hledejte cokoli s názvem "Console", "Terminal" či s řetězcem "term" v názvu (Xterm, ETerm, TeraTerm apod.), případně i "Shell", a hledejte to na ploše, na liště, v menu apod. Kdo hledá, najde.
Xtermů si můžete pustit, kolik chcete, a nemusíte se už do nich přihlašovat, automaticky jste tam jako ten uživatel, který se přihlásil do Xek. Ale tím se teď nebudeme zabývat, pro cvičení je nejlepší pravověrná textová konzole.
Odhlásíme se příkazem exit nebo logout, často funguje i zmáčknutí Ctrl+D, xterm můžeme i jednoduše zavřít příslušným tlačítkem v pravém horním rohu (znáte z windowsů :)).
(Důležité!!! - nutné pro procvičování veškeré látky)
Jednou z největších výhod UNIXových systémů je možnost vzdálené práce na systému. Většinou sice nejde o aplikace "klikací" povahy (i ty jdou spustit, ale potřebujete na to rychlé připojení a smysl takové činnosti je většinou diskutabilní), ale zvyknete-li si na příkazový řádek a textový mód, můžete provádět vzdáleně veškerou činnost stejně, jako byste seděli přímo u počítače. Například: vzdálená správa systému (server zlobí a komu by se chtělo do práce nebo do hostingového centra, když to může diagnostikovat/opravit po GPRS z chalupy :)), vzdálené výpočty, nebo třeba pouhý pohodlnější přístup k mailu a datům naprosto odkudkoli (vhodné při paralelním využívání více počítačů).
Na unixový systém se typicky přihlašujeme protokolem ssh (dříve se používal telnet, ale protože není šifrovaný, ustoupilo se od něj). Na unixovém počítači je téměř vždy nainstalován ssh klient (program, který pustí uživatel a pomocí něj se loguje z tohoto stroje na jiný stroj) a velmi často tam běží ssh server neboli ssh daemon (rozdíl mezi těmito pojmy nyní nechme stranou) - program, který se spouští při startu (tedy uživatel to, zda běží, nemůže ovlivnit, výjimky týkající se neprivilegovaných portů teď nebereme v úvahu) a umožní, aby se někdo mohl přihlásit z jiného stroje na tento stroj. Zatímco ssh klient typicky není problém používat, ssh daemon nemusí běžet, a i když běží, může být přístup zvenku na něj omezen firewallem. Časté je, že zatímco ze stroje můžete cokoli (i když i odchozí provoz bývá někdy filtrován), na stroj se můžete přihlásit například jen z určených IP adres nebo vůbec. Stroj Kozel, na který se budete přihlašovat ze účelem procvičování, není v tomto směru omezen, na portu 22 běží ssh daemon, který přijímá konexe odkudkoli (ovšem ostatní porty hlídá firewall).
Z unixového stroje na jiný unixový stroj se přihlásíte prostým zadáním příkazu
ssh jmeno_vzdaleneho_pocitace
nebo ještě lépe (používáte-li různá uživatelská jména)
ssh vase_jmeno_na_vzdalenem_pocitaci@jmeno_vzdaleneho_pocitace
tedy speciálně já se ze svého stroje na Kozla přihlásím pomocí
ssh johanka@kozel.pohoda.cz
příkaz ssh vás poté (co se vás případně zeptá, zda má uložit hostkey, odpovíte yes) zeptá na heslo, a když ho zadáte správně, jste tam.
Vás ale asi nyní zajímá hlavně to, jak se na unixový systém přihlásit z Windows. Existuje několik různých implementací ssh klientů, můžete použít buď SSH Secure Shell (samorozbalovací archiv, který obsahuje ssh klienta a scp klienta (k přenášení souborů)), nebo putty (na stránce najdete jak samotného ssh klienta putty.exe, tak i scp klienta PSCP, výhodou putty je, že je není třeba instalovat a může se spustit přímo z webové stránky, což můžete využít, pokud nedůvěřujete lokálnímu počítači). Na počítačích v laboratoři by mělo být nainstalováno putty.
Pokud některého z windowsích ssh klientů pustíte, pravděpodobně budete muset v nějakém pořadí udat jméno počítače, na který se chcete přihlásit, uživatelské jméno, pod kterým se chcete přihlásit, případně si dejte pozor na port resp. protokol (chcete protokol ssh, port 22, někteří klienti mají implicitně nastaven protokol telnet, port 23), poté budete dotázáni na heslo. V mezičase budete možná dotázáni na to, zda chcete, aby si klient zapamatoval hostkey cílové mašiny, souhlasíte.
Pokud se vše povedlo, měli byste nyní ve svých Windows mít terminálové okno své konexe na server. Konexí si samozřejmě můžete otevřít několik a záleží na klientovi, zda ho budete muset pro každou konexi spouštět znovu. Konexi ukončujete stejně jako lokální přihlášení (viz výše), tedy příkazy exit, logout, Ctrl+D, zavřením okna apod.
Jak už jsem uvedla, je třeba si *co nejdříve* změnit heslo příkazem passwd - těm, kdo dostanou heslo vygenerované ode mě a uvidím, že si ho při prvním přihlášení nezměnili, zablokuju účet!
Pro první rozkoukání se po systému poslouží například příkazy:
change directory, s parametrem udávajícím absolutní nebo relativní cestu k adresáři vstoupí do onoho adresáře.
Relativní cesta je relativní vzhledem k tomu, kde se nacházíme, a může vypadat např. tak, že jsme-li ve svém domovském adresáři a máme v něm podadresář "texty", napíšeme cd texty a jsme tam. Nebo máme-li bohatší hierarchii, napíšeme např. cd texty/denicek/leden, a je-li cesta správná, přesuneme se do onoho adresáře.
Cesta absolutní začíná lomítkem /, které reprezentuje root adresář, vrchol kořenové hierarchie, prostě takové to C:\ na DOSu nebo Windowsech (analogie trochu pokulhává hlavně vzhledem k reprezentaci fyzických disků a oddílů, ale v tuto chvíli stačí vědět, že / je úplně nejvýš a nic výš už neexistuje). Zkuste napsat cd / a pak si příkazem ls prohlédněte, co vidíte. A zkuste si prolézt různé adresáře, většina z nich vás asi vyděsí :).
(Poznámka: V Unixu se pro oddělování adresářů vždy používá normální lomítko /, takže na windowsí zpětné lomítko \ neboli backslash prozatím zapomeňte, bude mít speciální funkci někde úplně jinde (v regulárních výrazech).)
Zde se hodí též uvést, že příkaz cd .. vás přesune v adresářové hierarchii o level výš, tedy ať už jste kdekoli, po konečném počtu opakování se dostanete na / :). Lze je také řetězit, buď více dvojteček za sebou, nebo dvojtečky a názvy adresářů, tedy když jsme např. ve výše zmíněném ~/texty/denicek/leden (vysvětlení znaku ~ viz dále), můžeme napsat cd ../.. a jsme zase doma, nebo cd ../unor, nebo cd ../../nocnicek/prosinec (vše jsou samozřejmě příklady předpokládající existenci zmíněných adresářů).
Váš domácí adresář neboli home se typicky (ale ne vždy!) nachází v /home/vase_uzivatelske_jmeno. Protože je to dlouhé a taky pro tu poznámku "ne vždy" se používá zkratka ~ tilda, která reprezentuje váš home. Tedy cd ~ vás (odkudkoli) dostane domů, cd ~/texty/denicek vás odkudkoli dostane tamtéž co cd texty/denicek přímo z vašeho homu. A aby to bylo ještě pohodlnější, prosté cd bez parametru vás přesune taktéž do vašeho homu. Mimochodem cd ~nekoho_jineho_username vás dostane do homu toho někoho, tedy cd ~johanka vás dostane do mého homu (nikdo ovšem netvrdí, že je čitelný :)).
(Každému jménu souboru nebo adresáře může předcházet absolutní či relativní cesta.)
Chcete-li se vykecávat s kamarády nalogovanými na tomtéž stroji, můžete použít:
Talk a finger můžete provozovat i na jiný stroj, ale na onom stroji musí běžet příslušný server (u talku i na vašem) a musí být puštěný přes případný firewall; ani jedno z toho není v dnešní době příliš pravděpodobné).
Unixový systém obsahuje konečné množství uživatelů, z nichž alespoň jeden je superuživatel neboli root ("root" se někdy říká i kořenovému adresáři /, tak se tím nenechte zmást). Uživatelé se rozlišují podle svého UID, což je číslo, ale vy s ním většinou nepřijdete do styku. Dále mají hlavně login name, primární grupu (viz dále) a domovský adresář. Root má UID 0, uživatelé typicky čtyř či pětimístné. Seznam všech uživatelů je v souboru /etc/passwd, nenechte se zmást názvem, hesla tam nenajdete (dříve tam byla, samozřejmě zašifrovaná, ale poté se kvůli vyšší bezpečnosti přesunula do samostatného souboru /etc/shadow, který by pro normální uživatele neměl být čitelný).
Je-li v souboru /etc/passwd více záznamů k jednomu UID, jedná se o aliasy, jeden a ten samý uživatel se pak může přihlásit pod různými jmény, ale má pořád stejná práva a z hlediska systému jde o totožného uživatele (systém pracuje vždy s UID, ne s login name, a pro potřeby komunikace s uživatelem přeloží UID na první login, co v /etc/passwd najde a k danému UID pasuje).
Za účelem snadnějšího vymezení práv existují také skupiny uživatelů, které jsou vyjmenované v /etc/group. Každý uživatel má alespoň jednu skupinu, jejímž je členem, je to tzv. primární skupina a je uvedena v jeho záznamu v /etc/passwd hned za UID (grupy se opět odlišují čísly (GID) a jejich názvy jsou jen pomůckou pro uživatele). Primární grupou bývá u běžného uživatele typicky 100 - users, případně jeho vlastní skupina s GID stejným, jaké je jeho UID. Má-li být uživatel členem dalších skupin, je jeho login name uvedeno v řádcích daných skupin v /etc/group. Pohled do uvedených souborů napoví :) K čemu jsou grupy dobré, se dozvíte záhy.
Každý soubor (adresář zde budiž chápán jako speciální druh souboru) má právě jednoho vlastníka, právě jednu skupinu a dále práva určená pro vlastníka, skupinu a ostatní. Vlastníkem je typicky ten, kdo soubor vytvořil, skupinou je primární skupina toho, kdo soubor vytvořil. Jako root můžeme jakýkoli soubor přivlastnit komukoli (chown novy_vlastnik.nova_skupina filename), jako uživatel můžeme svému souboru změnit skupinu na některou z těch, jejichž jsme členy (stejným příkazem, akorát novy_vlastnik musí být stále naše login name). Vlastníka i skupinu souboru se dozvíte v dlouhém výpisu ls -l, stejně tak práva.
Práva se přiřazují vlastníkovi, skupině a ostatním, prostě určujeme, co se souborem může dělat jeho vlastník, příslušníci skupiny, které soubor patří, a kdokoli jiný. Lze přiřadit právo pro čtení (read - flag r), zápis (write - flag w) a spuštění (execute - flag x). Význam flagu x je různý: u souboru znamená, že jej lze spustit, u adresáře, že do něj lze vlézt (a r u adresáře znamená, že lze vypsat jeho obsah). Pomlčka ve výpisu práv znamená, že toto právo není.
Demonstrujme na příkladu. Práva souboru, se kterým nemůže nikdo nic, vypadají takto: ---------. Práva souboru, se kterým může každý všechno, vypadají takto: rwxrwxrwx. Práva souboru, se který může vlastník všechno, člen grupy jej může pouze číst a spouštět a ostatní nic, vypadají takto: rwxr-x---.
(Pokud jsme členy více grup, je občas třeba pro "plné využití" práv jiné skupiny než naší primární se do ní nalogovat ("plným využitím" je zde míněno např. právo zápisu, to, aby se nové soubory přivlastňovaly této skupině apod.). To provedeme příkazem newgrp vysnena_skupina.)
Poznamenejme ještě, že právo x není nic platné, pokud se nejedná o adresář nebo o program/skript, u nespustitelného souboru to člověku žádný zvláštní požitek nepřinese, no a odebráním pouhého práva x u souboru také ničeho mimořádného, co se týče bezpečnosti, nedosáhnete, kdokoli, kdo má právo r, si soubor může zkopírovat a práva změnit u své kopie.
U adresářů je to jiné. Kdo má k němu jen právo r, nemůže nic. Protože obsah by si teoreticky i přečetl, ale dovnitř do adresáře nemůže. Kdo má práva r-x, může do adresáře vlézt a zjistit, co je v něm. A kdo má jen právo x, může do adresáře vlézt, ale nevylistuje si seznam souborů. Ovšem zná-li přesný název souboru, pro který do adresáře přišel, může s ním dělat cokoli (samozřejmě co mu dovolí práva k onomu souboru). Práva k adresáři rwx--x--x se dávají obvykle na home nebo na adresář s webem - nikdo si nevylistuje obsah, ale když řeknete kamarádovi "vem si ode mě z homu ten a ten soubor", může si ho zkopírovat.
Obvyklé kombinace práv jsou rw-r--r-- či rw------- pro normální soubory, rwxr-xr-x či rwx------ pro spustitelné soubory, u adresářů se nadto občas vyskytuje i výše zmíněné rwx--x--x. Nezvyklé kombinace typu r-x---rwx (proste "méně privilegovaná" část uživatelstva má vyšší práva) sice technicky smysl mají (zákaz přístupu určité skupině), nicméně příliš často se to nevyužívá (naživu jsem to neviděla ani jednou). Proto až na řídké výjimky bývají práva vlastníka >= práva grupy >= práva ostatních.
Práva se dají reprezentovat číselně - každá trojice rwx jedním číslem, tedy devítice písmen trojicí čísel. Právu r odpovídá číslo 4, w 2, x 1 a číslo odpovídající trojici je součtem těch flagů, které jsou zastoupeny. Příklady: rw-r--r-- = 644, rw------- = 600, rwxr-xr-x = 755, rwx------ = 700, rwx--x--x = 711. Jiné kombinace potkáte zřídka a snadno si je odvodíte.
Práva u souboru/adresáře může změnit vlastník nebo root, a to pomocí příkazu chmod. Já osobně ovládám pouze měnění pomocí čísel :), tedy chmod xxx filename kde xxx je číselná kombinace práv, tím natvrdo nastavíte všechna práva k souboru, tedy např. chmod 644 filename nastaví čtení a psaní pro vlastníka a čtení pro všechny ostatní (rw-r--r--).
Další možností změny práv je měnění jednotlivých elementů pomocí výrazu x+y či x-y (x je z množiny "u" user (zde ve významu vlastník), "g" group, "o" other, "a" all, y je z množiny "r", "w", "x", "+" znamená přidat právo, "-" odebrat), takže například "přidat všem právo read" se řekne chmod a+r filename, "odebrat grupě právo execute" se řekne chmod g-x filename apod.
Pokud se chcete dostat k souboru či k adresáři, musíte mít minimálně právo execute ke všem jeho nadřazeným adresářům. To je velmi důležité pro web, máte-li ho ve svém homu, je třeba dát na home právo execute všem, protože k vašemu webu budou přistupovat anonymní uživatelé. Stejně tak některé konfiguráky (ssh klíče např.) potřebují mít executovatelný váš home i adresář s konfiguráky pro všechny (aby se do některých nastavení mohla podívat zatím neprověřená druhá strana), naopak některé služby (opět se to týká ssh) jsou paranoidní a odmítají fungovat, pokud je na nějakém skutečně citlivém konfiguráku právo read pro všechny. To je jen obecná poznámka, že pokud se vám něco nebude dařit, nejdříve zkontrolujte práva :)
Ještě pro úplnou úplnost odbočím - při výpisu ls -l se vám na začátku řádku zobrazí nikoli devítice písmen s právy, ale desetice. První písmenko označuje typ souboru: pomlčka normální, "d" adresář, "l" symbolický link, zbytkem vás zatím nebudu mást.
Popíšu, co zhruba najdete v kořenovém adresáři / běžného Unixu. Neručím za to, že něco není Linux-specific :) V / by měly být jen adresáře, je-li tam nějaký soubor, je to většinou fosílie z nějaké krizové situace, např. když nebyl připojen home. To, kde je adresář ve fisesystémové hierarchii, nevypovídá naprosto nic o tom, kde se nachází fyzicky (máte-li více disků, síťové disky apod.) Zatím se na vše díváme jen uživatelsky, tedy nás to nezajímá. Až na výjimky, které uvedu, jsou adresáře obecně čitelné (tedy samy o sobě, některé soubory být nemusí), ale nezapisovatelné pro běžného uživatele.
Spoustitelné soubory lze rozpoznat utilitou file (file nazev_souboru), pokud výsledek sobě obsahuje řetězec executable, bude soubor zřejmě spustitelný :)). Nechce-li se vám poštvávat file na všechny soubory, lze binárku ("binárka" v Unixu je termín ekvivalentní označení "exáč" na Windowsech, i když je to samozřejmě jiný typ binárního kódu) snadno rozpoznat i pouhým okem, třeba podle executable práv (bez záruky, může je přiřadit kdokoli čemukoli), podle názvu, podle absence přípony nebo podle okem nečitelného obsahu :).
Kromě binárek (zkompilovaných programů) existují také skripty neboli interpretované programy. Skript je lidským okem čitelný a interpretuje se až za chodu. Občas má příponu (perlovský .pl, shellovský .sh apod.), ale není to pravidlem. Pokud ho chceme (a to jejich autoři typicky chtějí) pouštět samotný bez uvádění jména interpretu na příkazové řádce při každém spuštění, musí mít také executable flag a navíc jeho první řádek udávající cestu k interpretu vypadá např. nějak takto:
#!/usr/bin/perl
(perlovský skript) či takto
#!/bin/sh
(shellový skript, lze i #!/bin/bash a jiné shelly, o těch se dozvíte ve třetí pohádce). Jednoduše shrnuto, skript lze téměř jistě rozpoznat a přesněji zařadit pouhým, i necvičeným, okem :).
Binárku i skript pustíte tak, že napíšete na příkazovou řádku
Zde se vám bude hodit pěkná featura, a to je doplňování tabulátorem. Když napíšete na příkazovou řádku začátek názvu nějakého souboru a stisknete Tab, shell se jej pokusí doplnit, a to takto:
přičemž matchuje-li více možností, neobjeví se na první stisknutí Tabu nic (můžete ještě přidat písmenko, a tím zúžit výběr) a na druhé stisknutí se zobrazí všechny možnosti.
Speciální úkol pro velmi aktivní (nad rámec tohoto levelu): Nelíbí se vám vaše uživatelské jméno exXXXX? Chtěli byste jiné? Zjistěte, jakým příkazem vám jej mohu (konzistentně, tj. nikoli pouhou editací /etc/passwd a všech dalších konfiguráků, kde figurujete) změnit, napište mi přesnou syntaxi příkazu změny z vašeho původního jména na nové a já to provedu.
Textovým editorem zde nemíníme nějakou officoidní obludovinu se sazebními a standardizačními abmicemi :), ale obyčejný editůrek, do kterého načtete čistý text (tedy vidíte to, co je v souboru skutečně uloženo), nějak to změníte a opět uložíte jako čistý text. Takovým editorem je ve Windows například Notepad nebo i IDE Borland Pascalu apod.
Takový editor v Unixu nutně potřebujeme - na editování zdrojáků, konfiguráků apod. Pokud si zvykneme s ním editovat naprosto všechno a officoidní obludy vyřadíme ze svého šatníku, případně se s nimi omezíme pouze na čtení dokumentů od našich nedostatečně osvícených kamarádů či spolupracovníků, bude to úplně nejlepší. Pokud potřebujeme občas něco tisknout, aby to "vypadalo dobře", stačí si osvojit pár základních příkazů (La)TeXu.
Nepovinná mediálně-masážní odbočka (motivace k třetí pohádce):
Pro ukládání textových dat a jejich přehledné zpracování jsou čistě textové formáty zdaleka nejpohodlnější. Binární formát (.doc apod.) je něco, co přečte Word, který to uložil, při vhodné fázi měsíce možná i jiná verze, ale to je tak všechno. A pokud v takovém souboru chcete třeba provést hromadnou náhradu řetězce, musíte spoléhat na to, že to Word umí, pokud neumí, máte smůlu a makáte ručně.
Zato s daty uloženými v textových formátech už je onačejší práce (textovým formátem může být například HTML nebo obecnější XML). Namotivuji vás příkladem: máte hromadu textů všeho druhu (šablon dopisů, tabulek, poznámek apod.), a protože se vám vdala kolegyně a změnila jméno z Vomáčkové na Vopičková, chcete její jméno ve všech těchto dokumentech všeho druhu nahradit. Trochu si to zesložitíme, chcete to nahradit jen v dokumentech, jejichž datum poslední modifikace je novější než datum její svatby. A ještě lépe, pokud se v dotyčném dokumentu již vyskytuje jiná Vopičková, nahradíte naši Vomáčkovou jménem Vomáčková-Vopičková, v opačném případě prostým Vopičková.
Zamyslete se nad tím, jak dlouho byste to dělali ve Windows, pokud jsou dokumentů (několika různých druhů) stovky. Ale máte-li je v Unixu uložené v textových formátech, zvládnete to několikařádkovým skriptem v shellu (uvidíte ve třetí pohádce; věřím, že to jde na jeden řádek, ale nechci nic slibovat :)).
Konec odbočky.
No a kvůli údržbě a upravování textových souborů je třeba si osvojit práci s nějakým editorem. Rozhodně vám nebudu žádný konkrétní editor nutit ani vás z něj zkoušet, je to čistě vaše věc, editor je prostředek, ne cíl. Jediné, co po vás chci, je, abyste v průměrném případě zvládli rychle oeditovat soubor libovolným způsobem, a v horším případě (ořezaný systém apod.) věděli, jaké máte možnosti (prostě pokud si oblíbíte nějaký exotický editor, nebudu vám v tom bránit, ale sami musíme mít v záloze nějaké náhradní rozšířenější řešení).
Vi(m) je dnes asi nejrozšířenějším unixovým textovým editorem. Vi (VIsual editor) je původní verze, vim (VI IMproved) nová vylepšená, mimo jiné podstatně lépe ovladatelná. Těm, kdo se rozhodnou jít touto cestou, doporučuji defaultně používat vim a vědět, že pokud na systému není, mají zkusit vi. Základní ovládání obou je zhruba stejné, vim je dnes na unixových systémech téměř všude, a kde není, mělo by být vi (je k dispozici zdarma i pod Windows, pokud byste měli to nutkání). Dále budeme hovořit pouze o vimu.
Pokud se naučíte s vimem, neprohloupíte, ale je to běh na dlouhou trať. Vim je opravdu velmi silný editor, který se sice na začátku trochu cuká a pohodlného uživatele odradí, ale když se prokoušete počátečními obtížemi, zvládnete v něm běžnou práci snadno a postupně budete zjišťovat, jaké naprosto šílené operace s texty v něm lze rychle a intuitivně provádět. Já osobně používám nejprve vi a poté vim už asi šest let, a přesto cítím, že jsem teprve na začátku :) (tedy rozhodně vám tento editor nebudu nutit, zvlášť ne tehdy, pokud vás celý svět Unixu zatím nikterak neoslovil, ovšem myslíte-li to s Unixem vážně, začněte si s vimem co nejdříve, nebudete litovat).
Práci začneme příkazem vim nazev_souboru (lhostejno, zda soubor existuje, či nikoli). Pozor, skončení programu už není tak intuitivní. Skončíme tím, že zmáčkneme : (dvojtečku), tím se dostaneme do režimu příkazového řádku (to je jiný řádek, než ten v shellu, a začíná onou dvojtečkou vlevo dole), napíšeme za ni q, případně místo toho x, pokud jsme soubor měnili a chceme ho při odchodu uložit, a zmáčkneme Enter. Už z tohoto drobného úvodu vidíte, že to nebude nic pro citlivky :)
Pro každého, i pro toho, kdo si oblíbí jiný editor, je vhodné znát alespoň nejzákladnější základ. Ten spočívá v tom, že vim má tři základní módy (a pár dalších, o které se ale zatím nemusíte starat): normální (teda on není moc normální), vkládací a příkazový. Úplně pro začátek bude vaším cílem dostat se do vkládacího režimu, protože v něm můžete normálně psát do textu, pohybovat se šipkami apod. Toho dosáhnete (mimo jiné) zmáčknutím klávesy i jako insert, poté uvidíte (nebo neuvidíte, podle nastavení) v levém dolním rohu obrazovky nápis INSERT. Teď už je fajn a nemůže se vám nic stát.
To bohužel neplatí v normálním módu, kdy nápis INSERT dole nevidíte. V tomto módu nemůžete psát do textu, nýbrž s ním můžete provádět bůhvíjaké úkony (formátování, mazání řádek či kusů textu, vkládání apod.), skoro každá klávesa má nějakou funkci a různě se kombinují, takže když si nevšimnete, že nejste v insert módu a napíšete automaticky nějaké slovo, občas koukáte, co podivného editor vyvedl (vše se ale dá vrátit pomocí u (Undo), takže se nebojte (Redo, pokud si to zase rozmyslíte, se dělá pomocí Ctrl+r)). Vim v aktuální verzi má mnohaúrovňové undo, ale u vi bych na to nespoléhala, takže si rozhodně dávejte pozor na to, co děláte. Jednou nepěknou společenskou hrou, o které jsem kdysi slyšela, je otevřít programátorovi ve vi jeho nejdůležitější zdroják, a nechat ho, ať v normálním režimu napíše své jméno :)
No prostě normálního režimu se zatím bojíme a hledíme se hned dostat pomocí i do Insert módu. Tam si v klidu píšeme, a když chceme skončit, zmáčkneme Esc, čímž přejdeme do normálního módu, pak dvojtečku, čímž přejdeme do příkazového módu (přímo z insertu do příkazového módu přejít nelze, pokud si na to nenapíšete makro :)), a odejdeme zmíněným :q (to se samo o sobě povede jen tehdy, pokud jste soubor nezměnili, jinak dostanete warning či dotaz), nebo jeho variantami, které jsou: :wq (ulož a odejdi), :x zkratka pro totéž, :q! (odejdi a neukládej, vykřičník zde i jinde znamená "fakt jsem si jistej, že to takhle chci, tak už se mě neptej"), nebo taky :x! pro případ, že editovatel nemá právo zapsat do souboru, ale má právo si toto právo přidělit :). Dodejme, že samotné :w lze použít pro uložení kdykoli v průběhu editace, a uložit do jiného souboru lze pomocí :w nazev_souboru. Jinak pokud se do režimu příkazového řádku, tedy k dvojtečce, dostaneme omylem a chceme pryč, dosáhneme toho buď dvakrát zmáčknutým Escapem, nebo smazáním dvojtečky pomocí Backspace. Pak už jsme v normálním módu a do Insertu přejdeme, jak už umíme, pomocí i.
Tak tohle bylo takové "Hello world" ve vimu. Nyní už jste ve stádiu, kdy vás vim nepokouše, ale zatím asi nechápete, k čemu tolik patlání kvůli troše napsaného textu. Pravá síla editoru spočívá v normálním režimu a v příkazovém řádku. Ale zde už přenechám práci povolanějším a odkážu vás na skvělý Úvod do používání editoru vim od Pavla Satrapy. Jen jedna výjimka, která se vám bude asi hodit často - smazání řádku, na kterém stojíte, se provede v normálním režimu pomocí dd, tedy dvakrát za sebou stisknete d (nelekněte se, že se nikde neukazuje žádné info o tom, co už jste zmáčkli, uvidíte jen výsledek (pokud nemate zapnutou volbu showcmd)). O tom, jak smazat více řádků, jak je jinde vložit, a mnoho dalších věcí, se už dozvíte ve výše odkázaném textu.
Asi jste zvyklí z jiných správců souborů, že tlačítkem F4 můžete editovat. Integrovaný editor mc je vcelku přátelský, na vašem místě bych si na něj raději nezvykala jako na editor primární, ale sama jej s oblibou používám pro drobné a rychlé úpravy již existujících souborů. Mezi insertovacím/přepisovacím módem se přepíná klávesou Insert, řádka se maže Ctrl+K nebo Ctrl+Y, ukládá se F2 a odchází se F10 nebo 2x Esc.
Možná znáte oblíbený textový mailovací program Pine (používá se často na Unixu a někdy i na Windows), v tom případě znáte i jeho integrovaný editor pico (PIne COmposer). Z něj se vyvinula trochu vylepšená free verze Nano ("Nano's ANOother editor), oba editory jsou si velmi podobné. Na linuxovém (ale ne obecně unixovém) stroji bude s velkou pravděpodobností nainstalován alespoň jeden z nich, na Kozlovi je nano. Oba jsou jednoduché a intuitivní s nápovědnou řádkou.
Editor s ještě obludnějším ovládáním, než má vim. Je hodně rozšířený, ale ne tolik jako vim, má spoustu featur, které v životě nevyužijete, a odpovídající tomu je i delší čas startování. Víc vám o něm nepovím, protože víc nevím, ale v jeho užívání vám samozřejmě nebudu bránit :)
Je zbytečné, abych se o tomto tématu rozkecávala sama, když to opět podstatně lepším způsobem provedl Pavel Satrapa ve svém seriálu pro Root.cz. Zde si jen povíme, co všechno z jím probrané tematiky byste měli umět, co je důležité, na co si dát pozor apod., a uvedeme si několik příkladů.
Ze Satrapy si určitě přečtěte tyto kapitoly: 1. Znaky celou, 2. Základy opakování celou, 3. Nástroje podle potřeby (awk a Perl můžete vynechat), 4. Pokročilejší opakování a pozice celou, 5. Zapamatování celou, ale zatím nemusíte prožívat, když něco z toho nepochopíte, z 6. Modifikátory, druhy regulárních výrazů stačí znát modifikátor g a druhy regulárních výrazů a 7. Speciality Perlu můžete vynechat.
Při procvičování budou mít zatím výhodu ti, kteří si začali s editorem vim, ostatní budou muset přeskočit trochu dopředu a zkoušet regulární výrazy aplikovat v shellových skriptech či utilitách, které by zatím neměli znát :) (samozřejmě si je můžete zkoušet i v Perlu, PHP apod., pokud tyto jazyky umíte a máte rádi).
Syntax regulárních výrazů se naučíte z textu Pavla Satrapy odkázaného výše, zde se omezím na implementační detaily :). Ve vimu můžete vyhledávat tak, že v normálním režimu zmáčnete lomítko / a napíšete buď normální slovo, nebo regulární výraz. Nejlepší je vzít si na to nějaký dlouhý text a koukat, co to dělá. Nejjednodušší příklady:
/Pepa # vyhoví slovo Pepa /[Pp]epa # vyhoví Pepa a pepa /p.pa # vyhoví pepa, pípa, papa, pxpa, p3pa apod. /^Pepa # vyhoví Pepa na začátku řádku /Pepa$ # vyhoví Pepa na konci řádku /na.*dar # vyhovi nazdar, ale i "na silnici stojí radar" :)
Důležité je si zapamatovat, že regulární výrazy jsou hladové, což znamená, že namatchují nejdelší možný úsek textu: tedy při hledání výrazu na.*dar na textu nazdar, na silnici stojí radar se dá přednost namatchování celé této sekvence před prostým nazdar.
Kdo neumí a nechce umět s vimem, může si zkoušet grepat :), tedy použít utilitu grep (předbíháme). Použití je takovéto:
grep 'regularni_vyraz' nazev_souboru
a na výstupu dostanete všechny řádky souboru, které obsahují text vyhovující danému regulárnímu výrazu (ale která část řádky se přesně namatchovala, není označeno, takže názornější je hledání ve vimu).
Uvedu ještě příklad na zapamatování a nahrazení: chceme řetězec ex a čtyři čísla (váš známý login z Kozla :)) nahradit pouze těmito čtyřmi čísly. Ve vimu to provedeme z příkazové řádky takto:
:%s/ex\([0-9]\{4\}\)/\1/g
Což znamená: najdi řetězec ex bezprostředně následovaný čtyřikrát (\{4\}) opakovanou číslicí ([0-9]), přičemž tuto čtveřici si zapamatuj (kulaté závorky kolem ní). Celý tento řetězec (tedy ex i číslice) nahraď první (\1), v tomto případě jedinou, zapamatovanou skupinou znaků, tedy onou čtveřicí číslic. Jednotlivé části nahrazovacího příkazu (co matchuju, čím to nahrazuju, modifikátory) jsou odděleny lomítkem (normálním), modifikátor g říká: nahraď všechny výskyty na řádku, ne jen první (což je default).
Z příkazové řádky provedete pomocí sedu totéž takto:
sed 's/ex\([0-9]\{4\}\)/\1/g' nazev_souboru
přičemž pozměněný text dostanete na standardní výstup, soubor zůstane nezměněn. Můžete si to zkusit na /etc/passwd na Kozlovi.
Zde si představíme některé speciální typy souborů. Nejprve hrubý nástin unixovského způsobu uložení souborů: Data jsou na disku uložena v blocích nějaké velikosti. Každý soubor má jednu inodu (index node, I-node), což je jakýsi "obal" k tomuto souboru - obsahuje odkazy na všechny datové bloky souboru, tedy lze z ní vykoukat celý jeho obsah, a dále obsahuje kupu důležitých informací - vlastníka souboru, práva, čas poslední modifikace sebe samé i obsahu souboru apod. NEobsahuje název souboru ani jeho polohu v adresářové hierarchii. Prostě obsahuje naprosto všechny informace o souboru s výjimkou těchto dvou.
Inoda je ve filesystému jednoznačně reprezentována číslem a prostor vyhrazený na inody je nějakým způsobem omezený, takže se ve velmi exotických případech (hromada malých souborů apod.) může stát, že na disk již nelze zapsat, i když tam je fyzicky místa dost, protože dojdou inody. Ale mně se to ještě nestalo (Mirkovi tuším už jednou jo :)).
Adresář je specialní typ souboru. Má tedy také inodu, která ukazuje na normální datové bloky na disku, no a tyto datové bloky obsahují (v celkem rozumné téměř textové formě, např. na Solarisu lze prohlédnout pouhým okem, Linux to má workaroundnuté) prostě a jednoduše seznam souborů v tomto adresáři - vždy dvojici název souboru a číslo inody. Jinak řečeno, tento seznam každou svou položkou říká "tento balíček data+jejich vlastnosti (soubor reprezentovaný svou inodou) bude ve mně vystupovat pod tím a tím názvem". Z výše uvedeného je zřejmé, že adresáři je naprosto jedno, které jeho položky jsou soubory a které podadresáře - ukazuje pouze na inody, se kterými se pracuje se všemi stejně, a dál už ho to nezajímá. Až uvnitř těchto inod jsou informace o typech souborů a odkazy na jejich data, a to se začne řešit, až když člověk chce přímo s těmito soubory pracovat.
Seznam souborů v adresáři s čísly jejich inod můžete získat příkazem:
ls -i
resp.
ls -ai
pokud vás zajímají i soubory skryté (zajímají, ale k tomu až později)
Pro úplnost si řekněme ještě o tom, jak je to s některými typy speciálních souborů, konkrétně se zařízeními (devices v /dev) (ostatní speciální soubory, sokety, pojmenované pipy apod., vás asi zatím nezajímají). Zařízení mají také inodu se všemi featurami stejně jako normální soubory, ale místo odkazů na data je specifikován driver kernelu, který případné vstupy a výstupy ošetřuje. Nicméně podstatné je to, že pro filesystém jsou to pořád soubory (kvůli té inodě), až pro někoho snažícího se pracovat přímo s nimi se začnou jevit jinak.
Pokud jste si celou situaci zkusili nějak představit, jistě vás napadají zvídavé otázky (pokud nenapadají, přečtěte si předchozí text prosím ještě jednou). Otázka č. 1 by mohla znít např.: Může existovat inoda, na kterou odkazuje více adresářových seznamů? a otázka č. 2 třeba: může existovat inoda, na kterou nikdo neodkazuje? (Pozn.: možná někdo zauvažoval i o tom, zda jsou všechny datové bloky podchyceny v nějaké inodě, resp. zda může inoda např. ukazovat na neexistující blok - zde pes zakopán není, není-li filesystém poškozen, mělo by to v tomhle směru být v pořádku, tedy nový blok přibude, právě když o něm nějaká inoda "ví", stejně tak zmizí, právě když zmizí i odkaz z příslušné inody. Dále se asi nestane, aby se dvě inody přetahovaly o jeden blok :)).
Odpověď na otázku č. 1 je pro začátek zase otázka - "a proč by nemohla?" Podívejte se na následující prohlídku inod v testovacím adresáři. Testovací adresář neobsahuje téměř nic, pouze podadresář "novy".
johanka@chroustalka:~/test$ ls -ai 132722 . 53161 .. 132723 novy johanka@chroustalka:~/test$ cd novy johanka@chroustalka:~/test/novy$ ls -ai 132723 . 132722 .. johanka@chroustalka:~/test/novy$
Položky . a .. už dobře znáte. . (tečka) reprezentuje aktuální adresář (proto ./ spouští soubor v aktuálním adresáři) a .. (dvě tečky) reprezentují nadřazený adresář (proto cd .. vyleze o level výš). Už z tohoto popisu je zřejmé, že pojmy . a .. jsou vysoce relativní (něco jako "já" a "moje máma" - záleží na tom, kdo je vysloví). Prohlédněte si pozorně čísla inod v příkladu výše. Asi jste si všimli, že číslo inody reprezentující aktuální adresář ve výpisu adresáře test je stejné jako číslo inody reprezentující nadřazený adresář ve výpisu podadresáře novy. No však by taky bylo divné, kdyby to tak nebylo, protože adresář test skutečně JE nadřazeným adresářem svého podadresáře novy :). Stejně tak inoda adresáře novy ve výpisu adresáře test je stejná jako inoda aktuálního adresáře v adresáři novy. Opět je to logické. A je to také odpověď na první otázku - ano, dokonce na každém kroku narážíme na inody, na které existuje více odkazů zevnitř adresářové hierarchie, a dokonce odkazů s různými jmény - je to úplně každý adresář.
Teď už tedy máte i teoreticky podloženo, proč příkaz ./program (existuje-li samozřejmě) v adresáři novy provede totéž co novy/program v adresáři test - už při interpretaci první části cesty se dostaneme na inodu stejného adresáře, a pak už je to jedno, odkud a co jsme spouštěli.
Teď je vhodná chvíle pro vysvětlení pojmu hardlink. Hardlink je právě odkaz z listingu nějakého adresáře na inodu souboru (podadresáře) v dotyčném adresáři obsaženého. Soubor (zde ve smyslu inoda) má tolik hardlinků, kolikrát je na něj odněkud odkázáno. Rozmyslete si, kolik minimálné hardlinků má každý běžný adresář.
Jsou dva - jeho vlastní . ("já") a od rodiče ("moje dítě"), tedy z výpisu obsahu nadřazeného adresáře. Přičemž z každého podadresáře vede na "maminku" další jeden hardlink (..), takže počet hardlinků je minimálně 2+počet podaresářů. Implikaci lze obrátit (11 hardlinků => asi bude mít 9 podadresářů), neboť hardlink na adresář nemůžete ručně vyrobit (technologicky to problém není, ale mohlo by to způsobit neodhalitelné zacyklení adresářové struktury, a proto je to zakázáno).
Dosud jsme se bavili jen o adresářích, na kterých jsme si demonstrovali vcelku přirozený význam hardlinků. Nahardlinkovat lze ovšem i soubor (v tomto případě žádný "přirozený/od výroby" příklad nemám, dělá se to ručně, což na rozdíl od adresářů zakázáno není, protože to žádné podobné problémy nezpůsobuje). Hardlinky se (teď, když existují symlinky, viz dále) nepoužívají často, většinou pouze ve speciálních případech, já jsem za šest let práce s Unixem smysluplně použila hardlinky jen jednou.
Když napíšete ls -l a podíváte se na druhou položku výpisu (mezi právy a vlastníkem), uvidíte číslo, které udává právě počet hardlinků na soubor. U souboru je to typicky 1, u adresáře 2 a více, jak jsme si zdůvodnili výše. Nahardlinkovat existující soubor (existuje někde pod nějakým názvem) na jiný (jiné místo, jiný název, nebo obojí) lze příkazem ln (link):
ln puvodni_vyskyt[s_cestou] novy_vyskyt[s_cestou]
tedy např.
ln ../soubor kopie
nalinkuje "soubor" z nadřazeného adresáře do "kopie" v aktuálním adresáři.
V tuto chvíli lze na tentýž soubor přistupovat z více různých míst. Ale pořád je to jeden a ten samý soubor - zabírá jedno místo na disku, má jednu inodu, má jedna práva (takže když si nahardlinkujeme soubor, na který práva nemáme, tak je pořád nemáme), a když se změní, projeví se změna ve všech umístěních. Nejde tedy o kopii, pouze o "alias". Jeho počet hardlinků se zvětší o jednu a jeví se stejný při listingu z kteréhokoli umístění.
Počet hardlinků je také jednou z informací, které jsou uloženy přímo v inodě. Ale ona ví jen číslo, neví už, odkud ty hardlinky vedou. Něco jako když si nahlásíte trvalé bydliště, tak zákon velí, že úřad majiteli řekne, že počet osob v jeho domě se zvětšil o jednu, ale neřekne mu, o kterou. Stejně tak když z ls -l zjistíte, že soubor má více než jeden hardlink, tak víte akorát "a má tam kamaráda!", ale nevíte, kde a kterého a nemůžete to nijak triviálně zjistit (pouze systematickým prohledáním celé adresářové hierarchie na číslo jeho inody).
Pokud soubor smažete, smaže se nejprve pouze příslušný hardlink (přes který jste na soubor odkázali), tedy zmizí položka z listingu adresáře, a inoda si o jedna zmenší počet hardlinků. Takže soubor jakoby zmizí z adresáře, ve kterém jste provedli výmaz, a má-li nějaké hardlinky jinde (tedy figuruje-li ještě v jiných adresářích někde jinde, či ve stejném adresáři pod jiným jménem apod.), bude teď u nich o jedna menší číslo udávající počet hardlinků. Obsah souboru zůstane existovat nezměněn, stejně tak inoda.
Protože běžný soubor má jen jeden hardlink, provede se v tomto případě toto: zmizí položka z adresářové hierarchie a inoda si zmenší počet hardlinků na 0. Řekne si - ha, nula, to bych měla i s celým souborem zmizet. Pokud soubor zrovna není otevřený, zmizí. Pokud otevřený je, vyčká, až bude uzavřen, a pak taky zmizí. Proto například pokud smažete velký soubor, se kterým se ale stále pracuje (typicky log), musíte si pohlídat i restartnutí služby, aby se soubor uzavřel a mohl skutečně zmizel, jinak bude místo na disku zabírat pořád (a to je ten případ z otázky č. 2 - běžně inoda, na kterou nikdo neodkazuje, neexistuje, dočasně však existovat může).
Jestli vám přišly hardlinky dosud jako nějaký hnus, máte pravdu. Krom toho, že jsou trochu hůře pochopitelné, mají i různá omezení (mohou např. ze zřejmých důvodů fungovat pouze v rámci jednoho zařízení resp. filesystému, dále byla zmíněna nemožnost vytváření ručních hardlinků na adresáře). Proto byly implementovány symbolické linky, které fungují trošku jinak, ale jejich použití je většinou snazší a také snáze pochopitelné než u hardlinků.
Symlink je maličký soubůrek obsahující pouze tuto informaci: "já nejsem nic, já jsem jen směrovka, použij místo mě ten a ten soubor (nebo adresář, je to jedno) tam a tam". Symlink snadno poznáte na pohled (v ls -l je l jako typ souboru a u názvu souboru přímo uveden target, v midnightu mu předchází ~ a target se zobrazuje v dolní stavové řádce, často bývá barevně odlišen). Symlink má defaultně 777 práva, protože nenese žádná data, důležitá jsou práva na target souboru. Na rozdíl od hardlinků je tady ovšem jasně odlišeno, že existuje právě jeden originální, jediný a pravý soubor a na něj může vést kupa symlinků. Target souboru je to jedno, na rozdíl od hardlinků o tom ani neví, že na něj někdo ukazuje.
Když nějak operujete s "obsahem symlinku" (zapisujete do něj, čtete apod.), operujete ve skutečnosti s target souborem. Když operujete se symlinkem jako takovým (mazání, kopie, přesun), operujete s touto směrovkou, tedy na target soubor to nemá vliv. Když symlink smažete, smažete opravdu jen směrovku, tedy target souboru se to nedotkne. Naproti tomu když smažete target soubor, stane se symlink neplatným (totéž samozřejmě tehdy, když ho rovnou nasměrujete na neexistující soubor), projeví se to ve výpisech např. červenou barvou, vykřičníkem či blikáním, a s obsahem samozřejmě nelze pracovat.
Symlink může být absolutní, nebo relativní ve smyslu absolutních a relativních cest, tedy podle toho, jakou mu zadáte při výrobě cestu, té se bude držet. Když mu zadáte ../neco, nijak si to neexpanduje a pokaždé hledá neco v nadřazeném adresáři, což může být výhoda i nevýhoda. Výhoda to je, pokud se třeba celý home šoupne úplně jinam, tak by se případné absolutní cesty musely měnit, nevýhoda to je, pokud se soubory dostanou "od sebe", tedy link nebo target půjde jinam, v tom případě se zase relativní cesty musí měnit. Tedy absolutní a relativní cesty se používají podle okolností, podle toho, jaké očekáváme do budoucna změny v adresářové hierarchii. Pokud chceme dva soubory se sebou navzájem svázat bez ohledu na možné přesuny jich obou, nezbývá nic jiného než použití hardlinků, a to je dle mého názoru také jediný případ, kdy je opravdu vhodné je použít.
Chceme-li editovat "opravdický obsah symlinku", tedy změnit jeho target, nevím, jestli to vůbec nějak přímo jde, nejsnazší asi je nevhodný link smazat a vytvořit nový. Midnight má volbu "File/Edit Symlink", ale tipla bych, že to dělá úplně stejně dřevně. Pokud mě někdo poučí a ukáže mi nativní příkaz na editaci targetu symlinku, budu jedině ráda :).
Normální člověk (program) je zvyklý, že píše se na klávesnici a výsledky se zobrazují na obrazovce. Takto to většinou opravdu je, ale je vhodné pohlédnout na věc trochu obecněji (jak už možná víte z céčka nebo z DOSu), tedy operovat s pojmy standardní vstup (stdin, u mě doma napsáno na ledničce) a standardní výstup (stdout, u mě doma napsáno na záchodě). Pojmy pocházejí ještě z dob, kdy zdaleka nebylo samozřejmé, že vstup bude z klávesnice (mohl jít třeba ze sériové konzole ovládající daný stroj) a výstup bude na obrazovku (může jít zpět na onu konzoli nebo třeba na tiskárnu). Žádný prostý (řádkový) program tedy nepočítá natvrdo s klávesnicí a obrazovkou, nýbrž čte ze standardního vstupu, vypisuje na standardní výstup, a co se těmito pojmy rozumí, to už rozhodne správce nebo uživatel (ačkoliv defaultně je to samozřejmě ona klávesnice a obrazovka).
Co to pro nás znamená? Například to, že když program vychrlí deset obrazovek hlášek, nemusíme truchlit nad jejich neprohlédnutelností, nýbrž si (samozřejmě již v okamžiku spuštění - jednou vychrlené hlášky jsou už (až na nějakou tu cache terminálu) v nenávratnu) prostě jeho výstup přesměrujeme do souboru a pak si výsledek v klidu prohlédneme. Program se chová pořád stejně, byl napsán tak, že neví o žádné obrazovce, a posílá data pořád na totéž virtuální místo, totiž stdout, což je pro něj neznámá díra, do které data sype.
Pojem "díra" se přesněji řekne file descriptor, tedy, jak je z názvu zřejmě, má co dělat s nějakým souborem. Ale program neví, s jakým souborem, vidí jen tu díru do něj (jako když lezete do tunelu na letišti a netušíte, ve kterém letadle nakonec skončíte :)). Na první přednášce jsem říkala, že v adresáři /dev jsou různá zařízení, která se "tváří" jako soubory. To jejich tváření se je dobré právě na tohle. /dev/console je totiž taky "soubor", akorát je to ve skutečnosti obrazovka. Když tam něco zapíšeme, tak pro nás jako lidi i pro naše programy ten zápis vypadá úplně stejně jako do jakéhokoliv jiného souboru, jenže na /dev/console ve skutečnosti sedí kernel, který vezme to, co jsme tam zapsali, a vypíše to na obrazovku.
Takže program má jako defaultní standarní výstup nastaven file descriptor /dev/console (nebo /dev/tty*, to je v tomhle případě asi jedno). Chceme-li mu ho změnit, provedeme to při spouštění souboru většítkem >, příklad
cat /etc/passwd > kopie_passwd
tedy přesměruje vypsání (cat) souboru /etc/passwd do souboru kopie_passwd v aktuálním adresáři (příkaz je samozřejmě celkem na nic, protože dělá totéž co cp /etc/passwd kopie_passwd, ale tím si jej nebudeme kazit :)). Doplním, že pokud soubor neexistuje, bude vytvořen, pokud existuje, bude přepsán (obojí samozřejmě jen tehdy, máme-li příslušná práva).
Podobným způsobem lze přesměrovat vstup (<), zde soubor se vstupem samozřejmě existovat musí. Unixová filosofie velí, že máte-li nějaký program, který nějakým způsobem zpracovává vstupní soubor a produkuje soubor jiný (kupříkladu filtr či konvertor z nějakého textového formátu do jiného), je čistší pracovat se standardním vstupem (z něj brát vstupní soubor) a standardním výstupem (na něj vychrlit výstupní soubor) než očekávat názvy těchto souborů jako parametry. Program by se tedy v důsledku měl používat raději takto:
program < vstupni_soubor > vystupni_soubor
než takto
program vstupni_soubor vystupni_soubor
První řešení sice na první pohled vypadá hnusněji, ale má své neodolatelné kouzlo, které si přiblížíme dále. (Není-li programátor líný, naimplementuje samozřejmě obě možnosti - většina standardních unixových programů takto funguje, defaultně bere stdin a stdout, ale lze jim jako parametry (buď bez ničeho, nebo za nějakou optionou) zadat jména souborů.)
Kouzlo používání stdin a stdout spočívá v možnosti provázat vstupy a výstupy dvou programů pomocí | (pipe, roura). program1 | program2 na příkazové řádce znamená "spusť program1, vezmi jeho standardní výstup a předlož ho jako standardní vstup programu2". Takto lze řetězit více programů, tedy program1 | program2 | program3 .. apod.
Abychom si mohli ukázat nějaké smysluplné použití, představíme si program grep. Grep dostane vstup a vypíše z něj ty řádky, které vyhovují danému regulárnímu výrazu (přesněji "jejichž podmnožinou je řetězec vyhovující danému regulárnímu výrazu"), zbylé zahodí. Vstup lze grepu předat dvojím způsobem - buď jako stdin, nebo mu jako parametr uvedeme název souboru, ve kterém má grepat.
grep johanka /etc/passwd
vypíše řádky z /etc/passwd obsahující řetězec johanka.
grep "ex[0-9]\{4\}" /etc/passwd
vypíše řádky z /etc/passwd obsahující nějaký vám dobře známý login. Předpokládám, že pokud jste minule pochopili syntax regulárních výrazů, je vám funkčnost příkazu grep zřejmá.
Teď se podíváme, jak je to s tím standardním vstupem. Z předchozího textu je doufám zřejmé, že varianty
grep johanka </etc/passwd grep "ex[0-9]\{4\}" </etc/passwd
fungují úplně stejně jako výše uvedené příklady. Program má volitelný parametr "název souboru", pokud je tento parametr neprázdný (první příklady), sahá do zmíněného souboru, je-li prázdný, podívá se na standarní vstup.
A číže výstup bychom mohli programu grep přesměrovat pomocí |? No třeba catu, který, jak víte, vypíše obsah souboru. Příkaz
cat /etc/passwd | grep "ex[0-9]\{4\}"
tedy udělá totéž co odpovídající příkazy předchozí, ovšem už jsme si na něm mohli demonstrovat funkci |. Nicméně hodláme-li příkazy řetězit dále, už se bez | neobejdeme a jinak to udělat nelze. Vzpomeňte na příkaz less, který vypíše vstup/obsah souboru po stránkách. O jeho vstupu platí totéž co u grepu, tedy může dostat jako parametr název souboru, v tom případě jeho obsah vypisuje po stránkách, nebo může dostat rovnou na stdinu nachrlená data, která jen "zdržuje", tedy bufferuje a stránkuje. Takže výše uvedený výpis studentíků z /etc/passwd si můžeme zpohodlnit zařazením lessu:
cat /etc/passwd | grep "ex[0-9]\{4\}" | less
zopakujme si, co tato sekvence dělá. Příkaz cat vypíše obsah souboru na svůj stdout. První | vezme catův stdout a předloží ho jako stdin grepu. Ten vezme vstup po řádcích a posílá dál (na svůj stdout) jen ty z nich, které obsahují řetězec vyhovující danému regulárnímu výrazu. Další | tento stdout od grepu předloží jako stdin lessu. A ten vezme svůj stdin a vypisuje jej po stránkách. Tentokrát už s jeho stdoutem nic neděláme, vyleze tedy normálně na obrazovku.
Místo lessení můžeme vygrepaný seznam studentíků třeba zapsat do souboru pro pozdější použití. Můžete si rozmyslet několik způsobů, jak to provést.
No nebudu vás napínat, napadly mě tyto:
cat /etc/passwd | grep "ex[0-9]\{4\}" > seznam grep "ex[0-9]\{4\}" /etc/passwd > seznam grep "ex[0-9]\{4\}" </etc/passwd > seznam
Syntaktický detail: za <, > a | může a nemusí být mezera, je to jedno.
Nyní je čas na pár drobných dodatků k celé problematice. Předně - základní file descriptory nejsou dva (stdin, stdout), ale tři (stdin, stdout a stderr). Stderr je standardní chybový výstup, tedy typicky chybové hlášky. Slušný programátor vypisuje normální výstup programu na stdout a chybový na stderr. V běžném případě se obojí stejně vysype na obrazovku promícháno (sype se to chronologicky), ale důležité je, že v případě potřeby lze oba tyto výstupy oddělit (typicky tak, že jeden z nich nebo oba někam přesměrujeme).
Příklad výstupu na stderr uvidíte, pokud třeba zkusíte jako běžný uživatel vypsat /etc/shadow. Dostanete hlášku, že permission denied, ale když zkusíte výstup přesměrovat
cat /etc/shadow >soubor
hláška na obrazovce zůstane a v souboru nebude nic, protože přesměrování se týka stdout a toto je právě stderr.
Běžně se s výstupy nakládá některým z následujících způsobů (teď nemluvím o obyčejné řádkové práci, kdy je vám jedno, kde se objeví jaká hláška, a když už se objeví, můžete na ni hned zareagovat, ale když se pouští složité skripty a vůbec se dělají vymakanější operace :)):
Například pokud dáme kompilovat velkou krávu program (OpenOffice.org, GTK apod.), kde to trvá mnoho hodin a kompilátor vychrlí mnoho stránek hlášek, má smysl už na začátku myslet na oddělení výstupů a jejich uložení do logů - protože když to po těch čtyřech hodinách spadne, jsou samozřejmě důležité i kompilační chyby nebo warningy, které se objevily už na začátku kompilace a my se teď na ně nedoscrollujeme. Na druhou stranu chyb bylo pár a ostatních hlášek tuna, takže má smysl výstupy oddělit a ukládat jen chyby, ve smíšeném výstupu bychom se jich v životě nedobrali.
Teď si tedy řekneme jak na to. Naše tři deskriptory mají každý číslo. Stdin 0, stdout 1, stderr 2. Těmito čísly na ně také odkazujeme. Při přesměrovávání stdin a stdout číslo nepotřebujeme, šipečky vědí, co mají defaultně dělat (ale když tam číslo přidáme, nic se nestane). Stderr jde defaultně na obrazovku, přesměrování stdin ani stdout (!!!) na něj nemají vliv. Chceme-li přesměrovat stderr, použijeme 2>, tedy akorát před běžné přesměrovátko stdoutu předřadíme dvojku, aby přesměrovátko vědělo, že teď se jedná o stderr. Takže při kompilaci provedeme např. toto:
make >normalni_hlasky 2>chybove_hlasky
a na konci budeme mít standardní výstup v souboru normalni_hlasky a chybový výstup v souboru chybove_hlasky.
Chceme-li naložit s oběma výstupy hromadně, přesměrujeme stderr do stdout, což se provede pomocí 2>&1, tedy "vezmi dvojku a přesměruj ji na adresu jedničky" (zde využijeme to, že stdout má číslo 1, dřív jsme to vědět nepotřebovali). Pozor! Pokud chceme stderr přesměrovat do stdout a s celkem pak něco provést, musíme na příkazové řádce nejprve uvést přesměrování stdout a pak až stderr, tedy
make >vsechny_hlasky 2>&1
opačně to nefunguje (nepovinná vysvětlivka), protože v tuto chvíli musíte zapomenout intuitivní představu dvou odpadových rour napojených na sebe :), a raději si uvědomte, že každý deskriptor je číslo - první přesměrování tedy přeplácne deskriptor stdoutu deskriptorem souboru, druhé pak dá do stderr "to samé, co teď má stdout" (tedy deskritptor stejného souboru). Tady v případě opačného pořadí by to vypadalo tak, že stderr by byl poslán na stdout (původní), pak by stdout byl poslán jinam, ale stderr by se o tom už nedozvěděl a posílal by data na původní stdout, tedy na obrazovku.
Výjimkou z výše zmíněného je, pokud se celý výstup (stdout i stderr) posílá dále do |, ta má vyšší prioritu, takže se vyhodnotí dříve, tudíž nezáleží na pořadí a spojení výstupů může být uvedeno jako první, tedy takto:
make 2>&1 | ...
Tím bychom snad měli stderr vyřízen. V běžné práci ho příliš často nevyužijete (leda při reportování bugů), umět s ním pracovat se hodí spíš pro pohodlnost ("aby to neopruzovalo na obrazovce").
Při čtení předchozího textu jste občas narazili na to, že někdo něco zahodí, a možná vás zajímá, jak to provede. K tomu existuje černá díra /dev/null (u mě doma napsáno na odpadkovém koši), kam můžete hodit cokoli a už to nikdy nenajdete :). Zase, "tváří" se to jako soubor, ale je to prostě díra, něco tam vypíšete nebo přesměrujete a tím to vyhodíte. Takže pokud vás některý z výstupů (standarní nebo chybový) něčeho ani v nejmenším nezajímá, dáte mu >/dev/null, pokud si filtrujete spamy a víte, že když je tam 3x Viagra a jednou Rolex Watch, fakt to nechcete ještě ručně kontrolovat, tak to pošlete místo do junk folderu rovnou do /dev/null apod. Netřeba asi připomínat, že co tam jednou pošlete, to už zpátky nedostanete :).
Poslední poznámka k věci se týká appendu. Jak už jsem uvedla, přesměrování do existujícího souboru tento soubor přepíše. Chcete-li appendovat, tedy přidávat na konec a nedotknout se existujících dat, použijte místo jednoduchého přesměrovátka dvojité, tedy >>. Přesměrujete-li výstup takto do neexistujícího souboru, nic se neděje, normálně se vytvoří jako u prostého přesměrovátka, rozdíl se projeví jen v případě, kdy soubor už existuje.
System message: Na tomto místě jsem zjistila, že to nějak nestíhám, takže od této chvíle se čísla pohádek nekryjí s čísly dýchánků (= stále jsme ve třetí pohádce, ale tohle se dělá až na čtvrtém dýchánku).
Obyč ozvěna. "Vypiš to a to" a ona to udělá.
johanka@chroustalka:~$ echo "Ahoj" Ahoj johanka@chroustalka:~$
Smysl začne mít v případě, kdy testujeme, zda někam můžeme zapisovat, například do zařízení (třeba na sériovém portu visí modem, my echneme nějaký řetězec na příslušný sériový port (pomocí přesměrování stdoutu, tedy echo "neco" >/dev/ttySX, a když modem zabliká, vidíme, že v téhle části komunikace není problém). Další, důležitější smysl je ten, že můžeme echovat i obsah proměnných. To uvidíte později.
Představili jsme si již utilitu grep, která ze svého vstupu vyčeše řádky obsahující řetězec vyhovující danému regulárnímu výrazu. Parametr -v ji přiměje k inverznímu chování, vrátí tedy řádky, které takový řetězec neobsahují. Příklady použití:
grep vyraz soubor ... | grep vyraz | ... grep -v vyraz soubor ... | grep -v vyraz | ...
Pěknou featurou grepu je, že mu lze parametrem zadat velikost kontextu, tedy aby ke každému vyhovujícímu řádku vytiskl daný počet předcházejících a/nebo následujících. To už najdete v manuálových stránkách :).
Další užitečným a mocným nástrojem je sed, jehož nejčastějším použitím (opět za pomoci regulárních výrazů) je nahrazení daného řetězce v textu na vstupu řetězem jiným a poslání výsledku na výstup. Dělá prostě totéž co :%s ve vimu, ale dělá to řádkově, může se tedy zařazovat do | apod. Dobrá zpráva je, že grep i sed používají stejný typ regulárních výrazů (klasické) jako vim, takže se vám to nebude plést. Syntax je sed 's/co_nahradit/cim/modifikator', kde položky co_nahradit a cim vypadají stejně jako u substituce ve vimu a modifikátorem s oblibou bývá již známé g (nahraď všechny výskyty na řádce, ne jen první nalezený), ale nemusí být uveden také žádný. Zapamatování a odkaz na něj se provádí taktéž stejně (\(\) a \cislo). Příklady použití:
sed 's/vyraz/jiny_vyraz/' soubor ... | sed 's/\(vyraz\)/\1neco_navic/g' | ...
Jistě jste sami z příkladů nahlédli, že stejně jako grep dokáže i sed zpracovat jak soubor zadaný parametrem, tak (nedostane-li parametr) standardní vstup.
Hlavička a ocásek, na výstup vypustí, jak názvy napovídají, zadaný počet úvodních nebo závěrečných řádků (případně bytů) vstupu (stdin nebo soubor). Default je 10 řádků. Příklady použití:
tail soubor head -n3 soubor # prvni tri radky tail -c10 soubor # poslednich 10 znaku ... | head [-nX] | ...
Tato utilitka se hodí pro prořezávání vstupu strukturovaného po sloupcích (třeba výpis ls -l nebo soubor /etc/passwd), za parametrem -d jí můžete zadat znak, který jednotlivé sloupce odděluje (:, /, mezera (pozor, musíte ji backslashovat) apod., default je tabelátor) a za parametrem -f čísla sloupců, které chcete vypsat. Další možnost je (zejména při pevném formátování, jaké má ls -l, kde se občas vyskytnou shluky mezer dělající neplechu) očísnout fixní počet znaků zleva a/nebo zprava pomocí parametru -b (byty) nebo -c (znaky), přesnou syntax najdete v manuálových stránkách.
Předvedu na názorném příkladu. Chceme vypsat názvy všech podadresářů aktuálního adresáře. Provedeme to tak, že uděláme ls -l, grepneme si řádky začínající na d (zde využijeme, co jsem vám u regulárních výrazů zapomněla říct - metaznaky ^ a $ značí začátek resp. konec řádky) a z výsledku pak nějak zkusíme vyříznout poslední sloupec. Ukážu neúspěšné i úspěšné řešení :)
johanka@chroustalka:~/ling/moje/vyuka/slides$ ls -l total 24 drwx------ 2 johanka root 4096 lis 18 11:59 CVS -rw-r--r-- 1 johanka root 344 lis 6 18:06 index.html drwx--x--x 3 johanka root 4096 lis 2 14:36 level01 drwx--x--x 3 johanka root 4096 lis 24 17:19 level02 drwx--x--x 3 johanka root 4096 lis 24 18:57 level03 -rw-r--r-- 1 johanka root 1277 říj 15 2003 vpred.jpg johanka@chroustalka:~/ling/moje/vyuka/slides$ ls -l | grep ^d drwx------ 2 johanka root 4096 lis 18 11:59 CVS drwx--x--x 3 johanka root 4096 lis 2 14:36 level01 drwx--x--x 3 johanka root 4096 lis 24 17:19 level02 drwx--x--x 3 johanka root 4096 lis 24 18:57 level03
johanka@chroustalka:~/ling/moje/vyuka/slides$ ls -l | grep ^d | cut -d " " -f 10
CVS
14:36
level02
level03
johanka@chroustalka:~/ling/moje/vyuka/slides$ ls -l | grep ^d | cut -b 46-
CVS
level01
level02
level03
V prvním řešení (poslední sloupec při oddělení mezerami) nám nadělala neplechu právě přebytečná mezera u 2. listopadu, proto jsme využili pro tuto situaci vhodnější oříznutí po znacích. Ale spoléhat na přesné množství znaků nějakého výpisu bývá vždy ošemetné, takže se takovéto situace snažte řešit raději jinak.
Vypsání určeného sloupce si předvedeme na souboru /etc/passwd. Budeme chtít vypsat reálná jména všech prostých (nesystémových) uživatelů, tj. takových, kteří mají alespoň čtyřmístné UID.
johanka@chroustalka:~$ cat /etc/passwd | grep ":x:[0-9]\{4\}"
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
johanka:x:6358:0:Jeanne d'Arc,UFAL,2 2191 4368:/home/johanka:/bin/bash
qiq:x:18535:100:Big Admin Spensta,CTSsss,,2 3535 7874:/home/qiq:/bin/bash
perm:x:17437:100:Martin Pergel,KAM:/home/perm:/bin/bash
Teď máme všechny řádky, které potřebujeme dále zpracovat (uvozovky kolem grepaného výrazu jsme použili, aby se shell nesnažil některé znaky sám expandovat, více později). Pokud z nich chceme jen reálná jména, rozdělíme si pro začátek řádky na pole oddělená dvojtečkami a vezmeme jen páté políčko každého řádku.
johanka@chroustalka:~$ cat /etc/passwd | grep ":x:[0-9]\{4\}" | cut -d : -f 5
Anonymous NFS User
Jeanne d'Arc,UFAL,2 2191 4368
Big Admin Spensta,CTSsss,,2 3535 7874
Martin Pergel,KAM
Zde nám ovšem dělají problém ještě finger informace (kancelář, telefon apod.), takže tento dočasný výsledek ještě rozdělíme podle čárek a vypíšeme první pole. Takže za | přivěsíme ještě jeden cut:
johanka@chroustalka:~$ cat /etc/passwd | grep ":x:[0-9]\{4\}" | cut -d : -f 5 | cut -d , -f 1
Anonymous NFS User
Jeanne d'Arc
Big Admin Spensta
Martin Pergel
a tohle už je kýžený vysledek :)
Find je utilita, která se dosavadnímu výkladu trochu vymyká, protože nezpracovává textový vstup, nýbrž (jak název napovídá) něco vyhledává, konkrétně soubory v souborovém systému.
Prostý find bez parametrů vrátí všechny soubory v aktuálním adresářovém stromě, prostě vše, co se nachází v aktuálním adresáři, jeho podadresářích, jejich podadresářích apod. Soubory jsou vypsány na stdout s relativní cestou, každý na novém řádku (tedy výstyp ideální pro další zpracování.
Findu lze jednak přikázat, že má hledat v jiném adresáři než aktuálním (bývá zvykem do parametru i tečku jako označení aktuálního adresáře psát, i když se psát nemusí), druhak zadat nějaké volby, typicky omezující podmínky na jméno, případně na jiné atributy souboru (čas vytvoření, práva apod.). Jeden z obvyklých příkladů použití: find . -name "*retezec*" najde všechny soubory v aktuálním adresářovém stromě, které někde ve svém názvu mají retezec. find /etc -name "*sh" najde například všechny soubory končící na sh (tedy typicky shellové skripty) v adresáři /etc.
Exotičtější použití můžete nastudovat v manuálových stránkách.
Asi něco jako "word count". Dostane standardní vstup, soubor nebo hromadu souborů a spočítá, kolik je v nich písmen, slov a řádek. To má význam jak přímý, tak přenesený. Přímý je ten, že občas když píšete článek nebo nějaké jiné literární dílo :), je počet slov či znaků zdola či shora omezen, tak se to hodí. No a přenesený: někdy nás nezajímá přímý výstup nějakého příkazu (který je rozdělen na řádky), ale prostě jen, kolik toho je - jestli nic, jedno, dvě, moc.
Parametry (viz man wc) mohou specifikovat, že chceme spočítat jen něco, kupříkladu wc -l řekne počet řádek. Může nás třeba zajímat (kvůli hledání duplicit), kolikrát se nějaký soubor téhož nebo podobného jména vyskytuje v adresářové hiearchii (a už je nám jedno, kde je). Spustíme tedy find, který každý vyhovující soubor vrací na novém řádku, tento výstup pustíme do wc -l a výsledkem je kýžené číslo. Následující příklad spočítá, kolik souborů s názvem končícím na conf se nachází v adresáři /etc.
root@zireael:~$ find /etc -name '*conf' | wc -l 225 root@zireael:~$
Další příklad je počet podadresářů aktuálního adresáře (s výpisem, abyste si to mohli ověřit přepočtem :)).
johanka@zireael:~$ ls -l total 116 -rwx------ 1 johanka ufal 88 kvě 19 2003 cz drwx------ 2 johanka ufal 4096 čen 8 2004 Desktop drwx------ 4 johanka root 4096 lis 26 14:00 evolution -rw------- 1 johanka ufal 78 úno 19 2004 hacky drwx------ 6 johanka root 4096 lis 23 14:40 konfiguraky drwx--x--x 9 johanka root 4096 pro 7 17:38 ling drwx------ 9 johanka root 4096 pro 9 11:24 misc drwx--x--x 5 johanka root 4096 pro 9 16:37 public_html drwxr-xr-x 7 johanka root 4096 pro 10 20:33 temproot -rw------- 1 johanka root 70918 pro 10 19:29 temp.tgz johanka@zireael:~$ ls -l | grep "^d" | wc -l 7 johanka@zireael:~$
Tady vidíte, jak lze stejné věci zjistit naprosto odlišnými způsoby :) (můžete na to jít i přes počet hardlinků aktuálního adresáře).
Nemusíte umět! :) Vlastne nic z toho, co tady píšu, nemusíte konkrétně umět, akorát si musíte umět poradit s úlohami, které se pomocí těchto nástrojů řeší. Nicméně mnohé nástroje jsou zaměnitelné, vyberte si proto takové, které vám budou vyhovovat nejlépe.
Awk se opět vymyká z dosud uvedených kategorií, neboť to není utilita, nýbrž celý programovací jazyk. Neznám nikoho, kdo v něm programuje :), ale pokud chcete, klidně jej při zápočtové písemce použijte, bránit vám v tom nebudu. Široce se používá pouze v několika konkrétních případech, jedním z nich je zpracování vstupu po sloupcích (něco jako příkaz cut), kde jako defaultní oddělovač je brána jedna a více mezer a sloupce se číslují zleva od jedičky.
Obecná syntax je takováto: awk 'regexp { prikaz parametry }', přičemž awk vezme standardní vstup, vygrepá z něj řádky vyhovující regexpu (ten je volitelný, tedy nemusí tam vůbec být), z nich pak podle uvedených parametrů vezme zvolené sloupce a předhodí je příkazu prikaz. Konkrétně příklad ls -l | awk '/^d/ { print $9 " " $2 }' vezme dlouhý výpis souborů (ls -l, vezme z něj jen adresáře (řádky začínající na d, regulární výrad /^d/) a nechá vytisknout na stdout (příkaz print) devátý a druhý sloupec výpisu (název a počet hardlinků), a to v tomto pořadí (tedy v opačném, než bylo původní). Mezera mezi musí být explicitně uvedena v uvozovkách, jinak awk sloupce lepí. Jak to vypadá:
johanka@chroustalka:~$ ls -l total 96 drwx------ 2 johanka root 4096 čec 22 22:40 Desktop -rw------- 1 johanka root 78 úno 19 2004 hacky drwx--x--x 9 johanka root 4096 pro 7 20:47 ling drwx------ 2 johanka root 4096 zář 28 17:47 Mail drwx------ 2 johanka root 4096 říj 2 18:23 obrazci drwx------ 11 johanka root 4096 říj 21 21:18 rule-language drwxr-xr-x 7 johanka root 4096 pro 3 16:02 temproot -rw------- 1 johanka root 59679 pro 7 21:26 temp.tgz johanka@chroustalka:~$ ls -l | awk '/^d/ { print $9 " " $2 }' Desktop 2 ling 9 Mail 2 obrazci 2 rule-language 11 temproot 7 johanka@chroustalka:~$
Tady možná ještě něco přibude, uvidíme.
Shell je ten, s kým si povídáte, tedy jemu říkáte příkazy a on je vykonává. To by mohlo působit dojmem, že shell je velký mocný pán úzce spjatý s jádrem systému, ale opak je pravdou; shell je obyčejný program, interpret příkazů, dá se snadno nahradit jiným modelem nebo úplně odstranit (například speciální účty pro BFU, třeba emergency servisní pro někoho, kdo systému vůbec nerozumí, nebo omezené jen na čtení mailu, stahování souborů, správu webu apod., nemusejí při nalogování spouštět shell, nýbrž spustí jen nějaký jednodušší obslužný program, aby BFU napáchalo co nejméně škody :)).
Shelly se nějak vyvíjely, tím vás nebudu zatěžovat, důležité je, že úplně první nejblbější shell byl prostě shell (sh) a dnešní nejrozšířenější je bash (Bourne Again Shell). Nové verze se občas objevují, ale ne moc často a typicky bývají zpětně kompatibilní, jinak by uživatelé vývojáře asi ukřižovali :). Bash dnes najdete téměř všude (na libovolném Unixu, nejen na Linuxu), takže si na něj klidně zvykněte, využívejte, co vám přináší, pokud byste mermomocí chtěli psát extra kompatibilní skripty, můžete jim forcnout, aby se interpretovaly jakoby tím starým shellem sh. Ten sice na novějších systémech už nebývá, ale bash jej umí emulovat (vede na něj symlink /bin/sh->/bin/bash a bash je tak chytrý, že pozná, jako co je volán, a podle toho se chová). (Jak to pozná, to se asi už nestihnete dozvědět, ale kdo na to přijde, dostane slevu na zápočtu :)).
Když napíšete něco na příkazovou řádku (což už je činnost vám důvěrně známá), je to příkaz pro shell. On to vezme a něco s tím provede (typicky je to příkaz "spusť ten a ten program s takovými a takovými parametry, vstupy výstupy apod.").
No a shell skript je zjednodušeně řečeno sada příkazů pro shell. Prostě když chcete provést několik příkazů za sebou, nemusíte je psát a odesílat jeden za druhým, ale můžete je všechny napsat na jednu příkazovou řádku za sebe oddělené středníky, a ony se po odentrování jeden za druhým provedou. Nebo je můžete napsat do souboru opatřeného příslušnou hlavičkou (#!/bin/bash, místo bashe lze dosadit libovolný jiný shell), každý na jeden řádek (newline je v tomto případě na rozdíl od běžných programovacích jazyků taktéž oddělovačem, takže středníky nejsou třeba), následně soubor učiníte spustitelným (právo x) a spustíte.
Příklad ekvivalentních způsobů vykonání několika příkazů:
johanka@chroustalka:~$ grep johanka /etc/passwd > soubor
johanka@chroustalka:~$ cat soubor
johanka:x:6358:0:Jeanne d'Arc,UFAL,2 2191 4368:/home/johanka:/bin/bash
johanka@chroustalka:~$
johanka@chroustalka:~$ grep johanka /etc/passwd > soubor; cat soubor
johanka:x:6358:0:Jeanne d'Arc,UFAL,2 2191 4368:/home/johanka:/bin/bash
johanka@chroustalka:~$
(V tomto posledním příkladu se samozřejmě předpokládá, že skript jsme sepsali již dříve, zde ho jen ukazujeme.)
johanka@chroustalka:~$ cat grepni_johanku.sh
#!/bin/bash
grep johanka /etc/passwd > soubor
cat soubor
johanka@chroustalka:~$ ./grepni_johanku.sh
johanka:x:6358:0:Jeanne d'Arc,UFAL,2 2191 4368:/home/johanka:/bin/bash
johanka@chroustalka:~$
Tohle celé není jen pro pohodlnost, existují případy, kdy to vlastně nejde jinak a příkazy od sebe nelze oddělit. Kupříkladu když na dálku operujete s firewallem nebo nastavením sítě a chcete říct mašině "shoď síť a zase ji nahoď", tak jí to nemůžete dost dobře říct ve formě dvou příkazů za sebou, protože poté, co odentrujete první příkaz (shoď síť), vám zhebne spojení a mašina už neví, že má tu síť zase nahodit, a vy nemáte, jak jí to říct, protože se tam už nedostanete. Kdežto když napíšete /etc/init.d/network stop; /etc/init.d/network start, tak to zvládne (rýpálkové nechte si poznámky o tom, že lze použít /etc/init.d/network restart, já to vím taky, ale byl to jen metodický příklad, můžu povědět historku o tom, jak jsem shazovala firewall špatným pořadím příkazů a tím jsem Kozla taky ustřihla od sítě :)).
Nicméně shell skripty se samozřejmě používají i k řešení jiných, podstatně složitějších věcí. Aby to bylo trochu akčnější, můžete v nich kromě pouhého spouštění příkazů vyvádět spoustu dalších kousků, například používat základní programovací konstrukce (if, for, while apod.) a také proměnné. Nicméně nejprve musíme vyřešit pár nudných technických záležitostí.
Pokud příkaz obalíme obrácenými apostrofy (`prikaz`), provede se ve vnořeném shellu a jeho standardní výstup nahradí položku `prikaz` v příkazové řádce. Například chceme-li jako argument jinému příkazu dát množinu souborů, jejíž seznam je uveden v souboru seznam, dáme do příkazové řádky na příslušné místo `cat seznam` (jde to udělat i líp a taky se to tak dělá, zde chci pouze demonstrovat funkci zpětných apostrofů). Příklad - v seznamu jsou uvedeny názvy souborů z /etc, ve kterých chceme hledat řetězec johanka:
johanka@zireael:~$ cat seznam
/etc/passwd
/etc/group
johanka@zireael:~$ grep johanka `cat seznam`
/etc/passwd:johanka:x:6358:0:Jeanne d'Arc,doma,:/home/johanka:/bin/bash
/etc/group:users:x:100:johanka
/etc/group:sprava:x:8000:johanka,root
/etc/group:ufal:x:6000:johanka
/etc/group:share:x:3838:johanka,qiq,perm,root
johanka@zireael:~$
Příkaz se chová úplně stejně, jako kdybychom seznam souborů v argumentu rovnou vypsali na příkazovou řádku:
johanka@zireael:~$ grep johanka /etc/passwd /etc/group
/etc/passwd:johanka:x:6358:0:Jeanne d'Arc,doma,:/home/johanka:/bin/bash
/etc/group:users:x:100:johanka
/etc/group:sprava:x:8000:johanka,root
/etc/group:ufal:x:6000:johanka
/etc/group:share:x:3838:johanka,qiq,perm,root
johanka@zireael:~$
Už dříve jsme si povídali o tom, že některé znaky mají občas někde (zatím v regulárních výrazech) speciální funkci a mezi jejich funkčním a prostě sdělovacím :) významem se rozlišuje předřazením \. V shellu to vypadá podobně, některé znaky na příkazové řádce má tendence interpretovat po svém (asi vás už napadne, že < a > bude považovat za přesměrování, pokud ho neujistíte o opaku) a jejich prostě sdělovací význam je třeba vysvětlit opět pomocí \. Pokud používáte v řádku regulární výraz, tak tam nastane samozřejmě přímo bitva shellu a programu, kterému je regulární výraz určen - shell se pokusí například expandovat hvězdičky apod. (Vsuvka: * v řeči shellu znamená cokoli (tedy nikoli "libovolněkrát opakuj jako v regexpech") a používá se hlavně v případech jako ls *, cat /etc/pass* (vypíše soubor passwd i jeho zálohy) apod., je to intuitivní, prostě jako v DOSu; totéž platí pro otazník, který zastupuje právě jeden znak. Konec vsuvky.)
Proto to, co nechceme, aby shell interpretoval, ale aby to prostě vzal, jak to je, a předal programu, kterému je to určeno, buď na příslušných místech (nebezpečné znaky) backslashujeme, nebo celé uzavíráme do uvozovek " nebo apostrofů '. Samozřejmě že případné další uvozovky či apostrofy uprostřed řetězce musíme backslashovat, aby je shell nepovažoval za ukončení závorkovaného/apostrofovaného řetězce. Uvozovky a apostrofy se drobně liší v tom, kterým znakům jejich řídící funkci nechávají, obecně platí, že apostrofy jsou "drsnější" (uvnitř je třeba si hlídat už jen případný nadbytečný apotrof), takže pokud vám něco z uvozovkami nebude fungovat, zkuste apostrofy (a naopak - apostrofy jsou totiž zase tolik drsné, že vám v nich nebudou chodit třeba shellové proměnné). Podrobnosti najdete na slajdech Michala Brandejse (viz doporučenou literaturu). Tam najdete také ledacos o shellovém programování, co vám neřeknu ani do pohádek nenapíšu, protože to nestíháme a taky to sama neumím :) (nebudu zkoušet, ale je pěkné ty věci vědět.)
Proměnné se nikterak nedeklarují, prostě se použijí a tím se deklarovaly (něco jako v Perlu). Pokud mluvíme o proměnné jako takové, používáme její název, pracujeme-li s její hodnotou, předřadíme názvu $ (příklady uvidíte později). Parametry z příkazové řádky se číslují $1 atd.:
johanka@chroustalka:~$ cat test.sh
#!/bin/bash
grep $1 $2
johanka@chroustalka:~$ ./test.sh johanka /etc/passwd
johanka:x:6358:0:Jeanne d'Arc,UFAL,2 2191 4368:/home/johanka:/bin/bash
johanka@chroustalka:~$
Přiřazení hodnoty proměnné se provádí pomocí jednoduchého = (jako v C) a kolem se nesmí vyskytovat mezery (!), v tomto případě používáme název proměnné bez dolaru.
Načtení jednoho řádku vstupu do proměnné či více proměnných se prování pomocí příkadu read (nazev promenne opet bez dolaru):
read a
načte celý řádek do proměnné a,
read a b c
rozdělí řádek na slova (skupiny znaků oddělené mezerami), první slovo načte do proměnné a, druhé do proměnné b a celý zbytek řádky (včetně případných mezer) do proměnné c. Je-li na řádku méně slov než tři, přiřadí se nenaplněným proměnným prázdná hodnota.
Typických použitím je for nazev_promenne in nejaky_seznam; do sada_prikazu; done
Uvedené středníky jsou důležité v případě, že následující slovo nedáte na nový řádek. Konstrukce dělá to, že vezme každou z položek seznamu (kde ho vezmem, o tom dále), přiřadí ji do proměnné, provede sadu příkazů mezi do a done, pak vezme další položku seznamu, opět přiřadí do proměnné, a provede tu sadu příkazů zase s ní. Kupříkladu chceme-li vzít všechny html soubory v aktuálním adresáři a vypsat je za sebe do jednoho velkého souboru, provedeme to takto:
for soubor in `ls *html`; do cat "$soubor" >> velky_soubor done
(V prvním řádku stačí též for soubor in *html; do, shell to sám expanduje na soubory v aktuálním adresáři vyhovující masce.)
Pro jistotu dodávám, že "velky_soubor" je přímo název souboru, ne proměnná.
Zde byl seznam vytvořen za pomoci provedení externího příkazu ls *html a využití jeho standardního výstupu. Seznam můžeme i vyjmenovat, pokud víme, že soubory se jmenují a.html a b.html, bude to vypadat takto:
for soubor in a.html b.html; do cat "$soubor" >> velky_soubor done
Teď už je alespoň jasné, proč seznam opravdu musí být zakončen středníkem nebo novým řádkem. Totéž platí pro posloupnost příkazů (v tomto případě jeden).
Uvedený příklad můžeme rozšířit, aby byla větší legrace [(C) PerM]. Třeba mezi jednotlivé soubory do toho výsledného velkého vložíme řádek ###, aby od sebe šly snadno rozeznat.
for soubor in `ls *html`; do echo "###" >> velky_soubor cat "$soubor" >> velky_soubor done
Tímto jsme si nenásilně demonstrovali význam příkazu echo. Dalším vylepšením může být, že před každým souborem bude krom oddělovače i uveden přímo jeho název:
for soubor in `ls *html`; do echo "### $soubor" >> velky_soubor cat "$soubor" >> velky_soubor done
Zhruba takto funguje program tar, který ode mě neznáte, ale odjinud možná jo :). Zkusíme to udělat ještě veselejší a zařadit všechny html soubory, které jsou k nalezení v aktuálním adresáři a jeho podadresářích, čímž také demonstrujeme funkčnost programu find.
for soubor in `find . -name *html`; do echo "### $soubor" >> velky_soubor cat "$soubor" >> velky_soubor done
Obě tyto konstrukce intuitivně znáte z jiných programovacích jazyků, nicméně zde fungují trochu jinak. Syntax je podobná, proto jsem je spojila do jednoho chlívečku. Konstrukce if vypadá obecně takto: if seznam; then seznam_prikazu; [ elif jiny_seznam; then jiny_seznam_prikazu;] [else dalsi_seznam_prikazu;] fi. fi na konci je důležité (stejně jako done u foru), středníky v případě neoddělení novými řádkami také. While cyklus má syntax: while seznam; do seznam_prikazu; done
Co znamená if, elsif, else, while apod., je vám asi jasné, stejně tak účel konstrukcí (if - pokud něco platí, while - dokud něco platí), ale asi marně hledáte podmínku a nechápete, co na jejím místě dělá nějaký seznam
Shell totiž sám o sobě podmínky vyhodnocovat neumí, volá na ně příkazy test nebo [ (viz dále). Seznam za if resp. while reprezentuje seznam příkazů, které se provedou. Pokud seznam tvoří příkaz jeden (typicky: "porovnej to a to a řekni, co ti vyšlo", jinými slovy "vyhodnoť podmínku"), zkoumá se jeho návratová hodnota, pokud je příkazů víc (oddělených středníky), zkoumá se návratová hodnota posledního. Podle toho, jaká tato návratová hodnota je, se podmínka jako celek bere buď jako splněná (a provede se příslušná větev), nebo jako nesplněná.
Zde je důležité podotknout, že v shellovém programování panuje jiná konvence návratových hodnot, než na jakou jste možná zvyklí. Obě tyto konvence mají své opodstatnění, nicméně důsledkem je pěkný bordel. Možná jste se někde setkali z tím, že 0 = false, 1 = true (třeba v logice, že :)). V shellu je to opačně, 0 = OK, true, !0 = kód chyby, která nastala (tedy false). Právě kvůli té možnosti vracet kódy chyb je třeba, aby pravda byla reprezentována nulou. Je vhodné, aby se i vaše programy či skripty, vracejí-li hodnotu, touto konvencí řídily.
Za účelem tvorby nekonečných cyklů :) se vám možná budou hodit drobné a triviální příkazy true a false, které nic nedělají, jen vracejí 0 resp. 1 (while true; do ... done)
Teď k tomu vyhodnocování podmínek. Jak už jsem uvedla, můžete použít buď program test, nebo hranatky [ ], obojí funguje stejně.
if test vyraz; then if [ vyraz ]; then
Mezery po otevírací hranatce a před zavírací jsou nutné (!), protože to jsou samostatné příkazy. Jak může vypadat výraz, najdete v man test, krom speciálních testů převážně na různé featury souborů jsou tam i klasická rovnítka, nerovnítka apod. Pomocí prostých = a != se dají porovnávat pouze řetězce (jsou/nejsou stejné), při porovnávání číselných hodnot použijete výrazy z následující tabulky::
význam | syntax |
---|---|
a == b | a -eq b |
a != b | a -ne b |
a < b | a -lt b |
a <= b | a -le b |
a > b | a -gt b |
a >= b | a -ge b |
Takže celá podmínka například na rovnost a a b vypadá takto:
if [ a -eq b ]; then neco_proved; fi
V podmínkové části však nemusíme testovat jen rovnost/velikost nějakých hodnot, někdy nás skutečně zajímá návratová hodnota nějakého příkazu. Kupříkladu pokud provedení cyklu má záležet na tom, zda se ve výstupu nějakého příkazu vyskytuje nějaký řetězec, zajímá nás jen návratová hodnota grepu, je nám už jedno, jak onen výstup vypadá a kde tam ten řetězec je. Potom se podmínka napíše přímo takto:
if prikaz | grep retezec >/dev/null; then echo "Je tam!" else echo "Neni tam!" fi
>/dev/null je tam proto, aby nás neobtěžoval výstup grepu a pracovali jsme pouze s návratovou hodnotou.
(Nepovinné, ale když se tím prokoušete, možná vám to pomůže k prozření :))
Následuje mnou aktivně používaný skript, který víceméně náhodou na jednom místě soustředí a demonstruje spoustu pěkných věcích vysvětlených v této kapitole, a tak ho sem dávám bez úprav, ale samozřejmě s vysvětlivkami.
Motivace: jak už jsem některým z vás na přednášce říkala, pracuji s jistou sadou souborů, přesněji řečeno lingvistických pravidel, na kterých kromě mě pracují i další lidé. Proto jsou všechna pravidla (soubory s příponou rlm) uložena v CVS Repository, kam jednotliví účastníci projektu zasílají své změny a stahují si odtamtud nové verze celé sady. Soubory se v repository různě upravují, přibývají nové, některé ubývají apod. Z tohoto důvodu (kompatibilita s repository a možnost pravidelného porovnávání s ní) musí být všechna pravidla (několik set souborů) i u mě v počítači v jednom adresáři. Nicméně abych se v nich vyznala (dělí se do mnoha různých skupin), potřebuju je sama mít roztříděna v adresářové hierarchii. Tyto zdánlivě neslučitelné potřeby jsem vyřešila hardlinky - každý soubor má jeden hardlink z adresáře se všemi soubory (který se porovnává s repository) a druhý z podadresářové hierarchie (sorted/..., se kterou pracuji já. Prostě každy soubor v tom "velkém" adresáři má kamaráda v podadresářové hierarchii (při změně jednoho se změní druhý apod.).
Když provedu cvs update (ve "velkém" adresáři), dostanu novinky z CVS. Typicky se některé soubory změní a některé přibudou. Bohužel změny provádí CVS tak nešetrně, že "rozbije" vztah souboru s kamarádem (zřejmě proto, že dočasně pracuje se souborem po jiným jménem), proto v tuto chvíli u změněného souboru mám aktuální verzi (s jedním hardlinkem) pouze ve velkém adresáři, zatímco "někde" v podadresářích je jeho kamarád, se stejným jménem, ale stará verze, a taktéž s jedním hardlinkem. Soubory, které přibudou, samozřejmě zatím nikde kamaráda nemají, mají tedy také jen jeden hardlink. Ostatní soubory (nezměněné) zůstávají se dvěma hardlinky a ve svazku se svým kamarádem.
Účelem skriptu tedy je 1) napravit první chybu (najít ztraceného kamaráda a přemazat ho hardlinkem nového souboru), 2) usnadnit mi zařazení kamarádů nových souborů (vytvoří hardlink na nový soubor a šoupne ho do adresáře sorted/unsorted :), odkud ho já už ručně přesunu, kam uznám za vhodné).
Teď ten skript (číslování řádek samozřejmě není jeho součástí, přidala jsem ho jen pro usnadnění pozdějšího vysvětlování).
1: #!/bin/bash 2: chmod 644 *rlm 3: for pan_na_holeni in `ls -l *rlm | grep "\- 1" | awk '{print $9}'` 4: do 5: radky=`find . -name "$pan_na_holeni" | wc -l` 6: if [ $radky -eq 2 ]; then 7: stary=`find . -name "$pan_na_holeni" | grep sorted` 8: rm $stary 9: ln "$pan_na_holeni" "$stary" 10: elif [ $radky -eq 1 ]; then 11: ln "$pan_na_holeni" sorted/unsorted/"$pan_na_holeni"; 12: else echo "$pan_na_holeni" 13: fi 14: done
Aktivnější samozřejmě mohou zkusit vylouskat bez mé pomoci :)
Pro ty méně aktivní ;)
Příklad zhruba takovéto složitosti (no, možná trochu jednodušší...) můžete očekávat (jako jeden z mnoha :)) u zápočtového testu, takže pokud máte pocit, že netušíte, která bije, je nejvyšší čas s tím začít něco dělat...
Tím bych uzavřela kapitolu o shellu. Nebylo tu řečeno mnoho velmi podstatných věcí, ale vyložit to nestíhám a je mi blbý chtít po vás, abyste si to prostě načetli a pak uměli; tohle jsou základy, se kterými si nějakou dobu vystačíte :). Takže nechávám pouze aktivním, dobrým materiálem jsou (jak už bylo řečeno) slajdy Michala Brandejse.
Následující příklady jsme více či méně stihli udělat na soustředěních, takže pro ty, co nezvládli pochytit, je dávám ještě sem. Všechny si asi nebudete moci přímo vyzkoušet, protože někde se hodí root (hledání souborů všech uživatelů), někde zase lokální mašina (přehrávání mp3), ale snad si alespoň uděláte představu. U ničeho z toho netvrdím, ze to nejde líp (uvítám bugreporty, rady apod.), u některých dokonce vím, ze jde líp, a dělám to rozmáznutě z didaktických důvodů :).
Přehrání všech mp3 souborů v aktuálním adresáři a jeho podadresářích:
Prasácká varianta (zfailuje na názvech souborů obsahujících mezery):
#!/bin/bash for pisnicka in `find . -name "*mp3"`; do echo "Nazev pisnicky je $pisnicka" mpg123 "$pisnicka" done
Čistší varianta:
#!/bin/bash find . -name "*mp3" |\ while read pisnicka; do echo "Nazev pisnicky je $pisnicka" mpg123 "$pisnicka" done(Backslash za pipou značí, že příkaz pokračuje na dalším řádku; pozor, za tímto backlashem nesmí být mezera, nýbrž přímo konec řádku!)
Vyrobení náhledů o straně 120 ke všem .jpg obrázkům v aktuálním adresáři a jejich umístění do podadresáře náhledy, prasácká varianta (pokud víme, že v názvech nejsou mezery):
#!/bin/bash for obrazek in `ls *jpg`; do echo "Vyrabim nahled k obrazku $obrazek" convert "$obrazek" -resize 120 nahledy/"$obrazek" done
Čistší varianta:
ls *jpg | while read obrazek; do ...
Spočítání, kolik souborů vlastní uživatel zadaný na příkazové řádce:
#!/bin/bash find / -user "$1" | wc -l
Spočítání, kolik souborů vlastní všichni studenti:
#!/bin/bash for clovek in `cat /etc/passwd | grep "6361" | cut -d : -f 1`; do echo "Jmeno vlastnika: $clovek" echo "Pocet vlastnenych souboru:" find / -user "$clovek" | wc -l done
Nalezení všech souborů obsahující daný řetězec (první parametr) a nahrazení všech výskytů onoho řetězce v nich jiným řetězcem (druhý parametr), prasácká varinta:
#!/bin/bash for soubor in `find .`; do pocet_vyskytu=`grep $1 "$soubor" | wc -l` if [ $pocet_vyskytu -ge 1 ]; then cat "$soubor" | sed "s/$1/$2/" > novy mv novy "$soubor" fi done
Několik poznámek: 1) zatímco "soubor" je název proměnné, "novy" je přímo (fixní) název souboru. 2) U sedu používáme uvozovky oproti obvyklým apostrofům, protože tohle je jedna z výjimek, kdy potřebujeme, aby shell sahal dovnitř parametru pro sed - údaje $1 a $2 jsou určeny pro něj, jsou to proměnné, které shell musí expandovat, než celý parametr předá sedu.
Jak už jsme se dozvěděli na začátku, existuje mnoho různých unixových systémů, všechny však dodržují jisté konvence. Drtivá většina toho, co jsme se zatím učili, funguje všude. Nicméně z regulárního výrazu zeď nepostavíš, takže je třeba, abyste byli připraveni na to, že obecné principy se sice nemění, ale názvy programů mohou být různé, mohou být na systémech v různých verzích, mít jiné parametry apod. (man je Tvůj kamarád). Stejně tak konfigurační soubory mohou být umístěny zcela jinde a mít zcela odlišnou syntax, než jste poznali na jiném systému. Proto je dobré pochopit hlavně principy, naučit se, jak rychle hledat řešení svých problémů, a to ostatní se už naučíte na míru systému, který se vám dostane do ruky.
V dnešní době žije mnoho komerčních Unixů (většinou spjatých s konkrétní platformou, i když ne nutně - může se vám stát, že na sunovském počítači najdete Linux, ale asi nepotkáte Solaris na i386 :)), dále je velmi rozšířen Linux v mnoha různých distribucích a různé otevřené verze BSD (FreeBSD, OpenBSD, NetBSD). Učit se pracovat s komerčním Unixem má smysl až ve chvíli, kdy před něj budete přímo posazeni; když někde shánějí admina perverzního komerčního Unixu, většinou ani nepředpokládají, že by s ním kandidát měl mít nějaké extra zkušenosti, stačí zkušenosti s libovolným Unixem. Používání otevřených BSD je tak trochu náboženství, jejich příznivci se za ně do krve hádají, ale v ostrém nasazení jsem je ještě neviděla, potkáte-li v komerční i akademické sféře i386 stroj s Unixem, v drtivé většině případů je to Linux.
Nicméně i linuxové distribuce se od sebe v mnoha věcech odlišují, jediné, co zůstává, je jádro, podle něj je Linux Linuxem. Tvůrci distribuce přibalí k jádru hromadu programů dle svého gusta, doplní k tomu nějaký ten systém konfiguráků, systém pro instalaci balíčků apod. a distribuce je na světě.
Jednou ze zásadních věcí, ve kterých se jednotlivé linuxové distribuce (a i jiné unixové systémy) liší, je způsob instalace programů. Vždy můžete stáhnout zdrojové kódy programu, zkompilovat a nainstalovat ("svatá trojice" ./configure; make; make install, make install musí být pod rootem), ale ne vždy to opravdu chcete, protože tímhle způsobem si do systému zanášíte trochu bordel - většina takto nainstalovaných programů se instaluje do /usr/local, ale není to úplně pravidlem, nemáte žádnou kontrolu nad verzemi jednotlivých programů, nemůžete program jednoduše odinstalovat, protože nevíte, které knihovny jsou jeho a které s někým sdílí apod. Proto tento způsob používejte, jen nejde-li to jinak.
Distribuce "balíčkovacího" typu mají nějaký balíčkovací systém. Nejrozšířenější je rpm, který používá nejen RedHat (Fedora), ale také třeba SUSE a Mandrake. V balíčku jsou zabaleny všechny soubory programu spolu s uvedením, kam se mají zkopírovat apod. Postup je jednoduchý, stáhnete soubor .rpm, obsahující daný program, napíšete rpm -ivh nazev_souboru a program se buď nainstaluje, nebo si postěžuje, že nemá splněny závislosti a potřebuje tu či onu knihovnu (ostatně takhle to vypadá, i když kompilujete, také je třeba mít splněny závislosti). Tento postup má mnoho výhod:
Z těchto důvodů je například vhodné držet se distribučního jádra a nekompilovat si vlastní, není-li to nezbytně nutné. K distribucím balíčkového typu patří i Debian, který používá vlastní formát .deb
Některé distribuce (Gentoo, Linux From Scratch, Source Mage Linux apod.) jdou jinou cestou a razí zásadu: vše kompilovat až na cílovém počítači. Důvodem je (aspoň myslím, s těmito systémy nemám administrátorskou zkušenost) zejména optimalizace na míru cílovému systému, jak rychlostně, tak například tím, že si uživatel přesně nakonfiguruje, které featury chce zakompilovat apod. Myslím, že s majitelem Gentoo je to jako s majitelem dvanáctistrunné kytary (půl života ladí a druhou půlku života hraje na nenaladěnou kytaru), oni zase půl života kompilují...ale nemůžu se k tomu zodpovědně vyjádřit, protože jak už jsem uvedla, na rozdíl od dvanáctistrunné kytary s tím nemám zkušenost. Tyto distribuce ovšem do původně neukázněné instalace ze zdrojáků přidávají jistý systém (sledování závislostí, vydávání opravených verzí apod.), takže nejvýznamnější výhody balíčkovacích systémů zůstávají zachovány.
Pokud chcete nainstalovat nějaký program, je vhodné zachovat tento postup:
Pro úplnost dodávám, že existují i tzv. zdrojové balíčky (v rpm jsou to .src.rpm) obsahující zdrojáky programu a spec soubor, ze kterých lze jistým postupem zahrnujícím kompilaci vyrobit normální balíček. Smysl je v tom, že můžete zapnout netradiční kompilační volby (zakompilovat třeba podporu něčeho, co v defaultním distribučním balíčku není), a zároveň zachováte výhody balíčkovacího systému (systém má přehled, co jste nainstalovali, s jakými závislostmi apod.), tedy provádíte s vybranými programy něco jako zdrojákové distribuce se všemi. Balíčkovací distribuce typicky nabízí ke všem binárním balíčkům i jejich zdrojákové varianty. Máte-li k programu zdrojáky a dostatek času a odvahy, můžete si sami zkusit zdrojový balíček vyrobit (taky jsem to už dělala :))
Na Unixu je bezva, že věci, na které jinde potřebujete složité nástroje a programy, tam jdou téměř samy. O síti to platí dvojnásob. Míra porozumění následující kapitole bude záviset na tom, kolik toho o síťařských věcech již víte, ale snad to s malým úvodem do nich (pokročilí mohou přeskočit) nějak zvládnete.
Asi víte, že existují nějaké MAC adresy, IP adresy a jejich překlady na jména. MAC adresa vypadá třeba takhle 00:08:A1:26:5A:2A a je to adresa přiřazená při výrobě jednoznačně konkrétnímu zařízení (síťová karta, WiFi karta apod.), měla by být unikátní, nicméně nelze to brát jako bezpečnostní prvek (na novějších kartách lze MAC adresu změnit jednoduchým příkazem). Většina služeb ovšem spoléhá na to, ze unikátní je.
Na unixovém systému můžete definovat různá síťová rozhraní (interfaces), každé z nich je reprezentováno svým názvem (lo, eth0, eth1, wlan0, dummy0 apod.) a IP adresou (z jakého rozsahu, to se dozvíte dále). Tato rozhraní mohou, ale také nemusejí být přiřazena konkrétnímu síťovému zařízení. Jedno rozhraní může být přiřazeno nejvýše jednomu zařízení, nicméně na jedno zařízení může být klidně navázáno víc rozhraní. Osvětlím: máte v počítači dvě síťovky. První tedy svážete s rozhraním eth0, druhou s eth1. Můžete ale klidně tu druhou svázat i s eth2, prostě akorát pak to, co má jít na eth1 nebo eth2, půjde na tu samou síťovku.
Rozhraní nemusí být také svázáno s ničím, to je případ loopbacku (lo, localhost, 127.0.0.1), který má každá mašina. 127.0.0.1 jsem prostě já, self reference, loopback mám vždycky, ať jsem připojená k síti, či nikoli, pokud ho smahnete, spousta věcí nebude fungovat. Proto když napíšete ifconfig (/sbin/ifconfig), který vypisuje nahozená rozhraní, vždy uvidíte alespoň loopback (ifconfig -a vypíše i nenahozená). Pokud z nějakého důvodu potřebujete ještě další rozhraní nesvázané s žádným hardwarovým zařízením, ale reprezentující jinou IP adresu, než je 127.0.0.1, vyrobíte si dummy rozhraní. To se hodí například v dynamickém routování, případné zájemce odkazuji na sérii svých článků Nejen o pruhovaném zvířectvu.
No každopádně podstatné je pochopit rozdíl mezi rozhraním a hardwarovým zařízením, což se doufám povedlo, takže jedeme dál. IP adresa vypadá třeba takhle 192.168.38.1 a má za úkol identifikovat počítač v síti. Počítač ji buď má nastavenou natvrdo (spolu s dalšími údaji o síti), nebo si o ni při bootu řekne DHCP serveru (což je samozřejmě systémovější řešení). IPv4 adres je konečně mnoho, spíše málo, jak je zřejmé z velikosti čísla, a proto jsou rozděleny do rozsahů veřejných a privátních. Veřejné IP adresy jsou unikátní v celém světě a používají se pro počítače přímo odevšad přístupné, privátní rozsahy (třeba zmíněných 192.168.0.0/16 nebo 10.0.0.0/8) se používají ve vnitřních sítích skrytých za nějakým typem internetové brány (nebo vůbec k Internetu nepřipojených), IP adresa je pak unikátní pouze v rámci oné sítě, ale nelze pomocí ní adresovat zvenku. Pro naše potřeby není třeba rozlišovat, z jakého rozsahu IP adresy jsou, jinými slovy naše nástroje a nastavení se chovají naprosto stejně, ať jsou spuštěna "venku" v Internetu, nebo v nějakém malém světě - vnitřní síti.
Poslední, co je třeba si představit, je jméno, domain name. Každá IP adresa může mít přiřazeno lidštější jméno, protože schopnost člověka pamatovat si IP adresy je omezená (Forstovo lemma říká, že si člověk zapamatuje nejvýše 20 IP adres). Jedné IP adrese můžete přiřadit libovolně mnoho jmen (všechna ukazují na tu jednu IP adresu, jako symlinky), stačí mít libovolný nameserver autoritativní pro dotyčnou doménu (ze které pochází jméno); ale reverzní záznam ("Jak se jmenuje tahle IP adresa?") může mít každá adresa nejvýše jeden, a musí být uveden v nastavení nameserveru toho, kdo spravuje daný rozsah IP adres. Proto také kozel může mít jedno ze jmen kozel.pohoda.cz, protože k tomu stačí mít přístup k nameserveru pro doménu pohoda.cz, ale aby se nám na dotaz "Jak je jmenuje 195.47.25.19?" objevilo "kozel.pohoda.cz", museli bychom ukecat providera :).
Jedno ze základních pravidel síťové diagnostiky říká, že kdykoli něco nefunguje (třeba browser neukáže stránku www.seznam.cz), zkusíme to nejdříve s IP adresou místo jména, protože vždycky může být problém s DNS resolvováním (ne nutně přímo u nás) a ne s připojením.
Takže úvod máme za sebou a můžete si teď představit jednotlivé užitečné nástroje (většinu z nich musíte pouštět s uvedením celé cesty /usr/sbin, některé fungují pouze pod rootem, u některých je část funcionality (diagnostická) přístupná pro kohokoli a část (nastavovací) pouze pro roota):
ping host (IP adresa nebo domain name) zjišťuje, jestli daný počítač žije, pomocí ICMP paketů (které jsou k tomuto účelu přímo určeny). Lze mu dát parametry na frekvenci, velikost paketu, typ paketu apod. Pokud počítač nepingá, ještě to nemusí znamenat, že nežije, může pakety zahazovat sám, nebo je může zahazovat někdo před ním. Pokud pingá, znamená to, že žije a síťové připojení funguje. Pomocí pingu lze zjistit nejen životnost, ale i průměrný packet loss.
traceroute host ukáže, kudy putují pakety k danému stroji. Je to užitečné buď tehdy, chceme-li zjistit, kde a jak je stroj připojen (kde se fyzicky nachází), nebo (zejména) tehdy, pokud nežije a my chceme vědět, kde spojení lehlo.
"Hackovací" :) nástroj. Ukáže, které síťové porty má cílový stroj otevřené a kterými síťovými službami s ním tedy lze komunikovat (pozor, nedozvíte se, které služby tam *skutečně* běží, výpis ukáže pouze to, co se na onom portu obvykle pouští, viz /etc/services; pokud tedy pustím na portu 80 ssh daemon, bude nmap stále tvrdit, že tam běží http). Pokud nmapnete kozla z lokálu, dozvíte se ledacos, pokud zvenku, nedozvíte se nic, protože je paranidně nastaven. Má to tak nastaveno mnoho strojů, navíc mnozí logují, kdo se jim pokusil scanovat porty, takže tento příkaz nepoužívejte bez rozmyslu :)
Oba příkazy překládají z IP adresy na domain name a zpět. S použitím rozličných parametrů mohou prozradit i více informací, např. nameservery autoritativní pro danou doménu apod.
Bez parametrů výpis nahozených síťových rozhraní počítače, s parametry jejich nahazování, shazování a nastavování.
Podává informace o paketech procházejících daným interfacem (-i interface, -n pro potlačení resolvování).
Sdělí přiřazení MAC adres k IP adresám v lokální síti.
Vypíše routovací tabulku (pro které IP rozsahy se pakety posílají na které rozhraní).
Lokální aliasy pro profláknuté IP adresy, aby se počítač nemusel pokaždé ptát nameserveru. Můžete si tam napsat, co chcete, těmto aliasům bývá typicky dána přednost před údaji zjištěnými od nameserveru.
Nameservery, od kterých se počítač postupně snaží resolvovat, search domena je případný údaj o tom, ve které doméně (či více doménách) se nacházíme (ke všem názvům počítačů se pak systém pokusí připojit tuto doménu a výsledek resolvovat).
Tak tím bych s pohádkami skončila. Asi budu průběžně bugfixovat, ale podstatná rozšíření se už v tomto semestru neobjeví :)
/* pozn. klo: autorkou tohohle ikonického textu o základech Unixu pro studenty je Johanka Spoustová/Doležalová. Protože po letech Pohádky z webu zmizely a bylo mi to líto, vyzvedl jsem v roce 2018 jejich archivní kopii z archive.org a aktualizoval o současné verze odkazů. */