Zápočet je možné získat splněním 2/3 domácích úkolů. Ty budou zadávány většinou po každém cvičení a bude je možné odevzdávat do začátku cvičení následujícího. Řešení posílejte jako textový soubor emailem.
Předmět emailů souvisejících s tímto cvičením prosím uvozujte textem [unix], (např. [unix] 1. úkol).
Zadání příkladů uvedená v závorce jsou bonusová a předpokládám, že většina lidí se k nim na cvičení nedostane. Naopak nestihnete-li na cvičení nějaký z neuzávorkovaných příkladů, doporučuji vyzkoušet si jej doma. Připadá-li vám, že je příkladů na cvičení příliš, dejte mi vědět; můžeme je probrat individuálně, příp. jich více můžu označovat za bonusové.
Pro vzdálený přístup k UNIXovým počítačům v Rotundě
můžete použít příkaz
ssh uzivatel@u-pl3.ms.mff.cuni.cz,
kde místo uzivatel uvedete svoje přihlašovací jméno
a místo u-pl3 příp. název jiného počítače v labu.
Na Windows by ke stejnému účelu měla sloužit aplikace PuTTY.
Máte-li jakékoliv dotazy, neváhejte se ozvat emailem nebo zeptat na cvičení.
a1, a1/b, a1/b/c, a1/d. Vytvořte příkazem cp kopii složky a1 pod názvem a2. Vše poté jedním příkazem smažte. Pokuste se najít co nejkratší řešení. (do cvičení 1. 3.)backup.sh, který zazálohuje aktuální adresář. Skript zjistí úplnou cestu k aktuálnímu adresáři, nahradí v ní lomítka za pomlčky a pod tímto názvem složku zkopíruje do složky ~/.backup/. Pokud zde již taková složka existuje, nejprve ji přejmenuje přidáním pořadového čísla. Pří kopírování zachovejte atributy souborů (čas, práva, ...). Použijte příkazy pwd, tr a příkaz cp s přepínačem pro přejmenování a číslování existujících souborů v Linuxové verzi příkazu. Po spuštění tohoto příkazu ve složce /tmp/a tedy dojde k jejímu zkopírování pod názvem ~/.backup/-tmp-a. Tento skript zatím není možné spouštět v domovském adresáři, protože by se pokusil zazálohovat i předchozí zálohy. (do 8. 3.) ~/.backup/tmp, poté této kopii změnte název na požadovaný příkazem mv s oním zálohovacím parametrem. (Ukázalo se, že se zálohování příkazem cp chová trochu jinak než jsem předpokládal.)realUserNames.sh, který ze souboru /etc/passwd nebo výstupu příkazu getent passwd získá skutečná jména uživatelů a jejich přihlašovací jména a vypíše je ve tvaru Pepa Novak:novakp abecedně setříděné. Budete potřebovat příkazy cut, paste, sort a mktemp pro vytváření dočasných souborů, všechny dočasné soubory na konci smažte. Řešení bez použití dočasných souborů je také vítano. (do 15. 3.)backup.sh pomocí POSIXových prostředků tak, aby bral jako parametry názvy souborů k zálohování. Zavoláním backup.sh . tedy docílíme podobné funkce jako u předchozí verze, nově ale bude možné zálohovat i běžné soubory, nejenom složky. Bude-li skriptu předáno více parametrů, měl by se chovat stejně, jako by byl spuštěn postupně s každým z těchto parametrů; nebude-li mu předán žádný parametr, měl by vypsat návod k použití a informaci, kam se vlastně zálohuje. Číslování souborů tentokrát vyřešte pomocí řídících konstrukcí shellu (while, if, for, ...). Kam přesně a pod jakými jmény se mají zálohované soubory kopírovat si zvolte sami, aby pro vás bylo používání skriptu pohodlné; jen případné odchylky od předchozí verze popište v těle emailu, ať vím, co je záměr a co chyba. (Mít v adresáři soubory začínající pomlčkou může působit problémy. Také vám nemuselo vyhovovat, že je složka .backup skrytá. Možná by už první vytvořený soubor mohl mít číslo. Nebo by se třeba místo pomlček mohly používat jiné znaky, příp. mezery by se daly taky něčím nahrazovat. Přidaná čísla by mohla mít vždy třeba 3 číslice (viz příkaz printf). Jakákoliv rozšíření jsou vítaná.) (do 22. 3.) promenna=$((promenna+1)).unpack.sh, který dostane jako jediný parametr název archivu (tar, zip, tar.gz, rar, 7z, ...) a zavolá příslušný příkaz k jeho rozbalení. Pokud by rozbalením mělo vzniknout více souborů, uloží je do nové složky v aktuálním adresáři; má-li vzniknout jen jeden soubor/složka uloží jej přímo do aktuálního adresáře. Vyberte si aspoň tři typy archivů, pro něž bude skript fungovat; k rozpoznání typu souboru použijte file a k větvení case. (do 29. 3.)rev, který vypíše každý řádek pozpátku. Napište v sedu skript tac, který vypíše řádky vstupu v opačném pořadí. (do 5. 4.)isPalindrome, který zjistí, zda je text na řádku palindromem, a napíše před něj ANO v kladném případě a NE v záporném. Palindrom je text, který se čte z obou stran stejně. Před odevzdáním vyzkoušejte na vstupech abccba, abcba a abcda. (do 12. 4.)postfixCalc.sh, který bude fungovat jako jednoduchá kalkulačka s postfixovou notací. Kalkulačka postupně vyhodnocuje jednotlivá slova ve výrazu: Je-li slovem číslo, přidá jej na zásobník; je-li slovem operace (např. sečtení), odebere potřebný počet operandů ze zásobníku (zde dva), provede s nimi danou operaci a výsledek opět vloží na zásobník. Pořadí operandů operátoru zůstává stejné, jako bylo při vkládání na zásobník (tedy opačné, než v jakém je ze zásobníku odebíráme). Při řešení se snažte upřednostňovat konstrukce shellu, např. sed a awk zde tedy nebudete potřebovat. Podrobné řešení vám pošlu emailem, je potřeba jej přesně dodržet; ozvěte se, pokud by email nedošel. (do 26. 4.)logger.sh, který bude v exponenciálně se zvětšujících intervalech (počínaje dvěma sekundami) vypisovat informaci o tom, že váš skript stále běží. Formát výstupu bude datum uživatel: hláška, kde datum je výstup příkazu date bez parametrů, uživatel výstup příkazu whoami a hláška náhodná hláška z vámi zvoleného seznamu (čím delší seznam, tím lépe; spočítejte si, kolik řádků bude vypsáno za jakou dobu). Skript spouštějte na u-pl16, můžete z něj volat skript /tmp/unix/randomLine.sh, který vypíše náhodný řádek ze svého vstupu, -- přečtěte si příklad použití na začátku tohoto souboru. Skript by tedy po spuštění měl vyčkat 2s, vypsat některou hlášku, vyčkat 4s, vypsat hlášku, vyčkat 8s, ... Důkladně otestujte, že skript funguje. Poté skript upravte, aby namísto standardního výstupu připisoval řádky do souboru /tmp/unix/log.txt. Využijte k tomu přesměrování výstupu příkazu echo, který řádky vypisuje, -- nepřesměrovávejte výstup celého skriptu. Skript spusťte na u-pl16 v aplikaci tmux, tu ukončete bez ukončení aktuální relace (skript bude stále běžet). Zkontrolujte v obsahu souboru log.txt, že váš skript i skripty ostatních fungují korektně. Skript tentokrát není potřeba posílat emailem, úkol budu počítat za splněný, jakmile budete mít v souboru log.txt na u-pl16 aspoň 10 řádků. Skript nechte běžet až do následujícího cvičení a mějte jej zálohovaný pro případ, že by došlo ke smazání log.txt a úkol jsem musel vyhodnocovat jinak. (do 3. 5.)htmlStructure, který dostane soubor html a má strukturovaně vypsat jeho obsah. Skript bude postupně zpracovávat jednotlivé tagy. Každý řádek výstupu bude začínat dvojnásobným počtem mezer, než je aktuální hloubka zanoření, poté bude následovat název tagu velkými písmeny a jeho atributy; alternativně může po bloku mezer následovat dvojtečka a text, který se nachází mezi tagy, neobsahuje-li jen bílé znaky, v tomto textu nahraďte posloupnosti bílých znaků jednou mezerou. Můžete předpokládat, že jde o XHTML dokument, každý tag má tedy buď uzavírací tag (<h1></h1>), nebo obsahuje uzavírací lomítko (<br/>). Příklad vstupu a výstupu naleznete zde. (prodlouženo, do 17. 5.)uniqFiles na rozpoznávání duplicitních souborů. Skript bude číst ze vstupu seznam souborů (jeden soubor na řádek), a pokud soubor s daným obsahem ještě neviděl, vypíše tento řádek na výstup. Z duplicitních souborů se tedy vždy vypíše ten, který byl na vstupu jako první. Předpokládejte, že soubory můžou být velké, na uložení jejich obsahu do proměnné není paměť a na jejich opakované čtení čas. Možný postup: Použijte sha256sum na vytvoření kontrolních součtů souborů (stejné soubory budou mít stejný, různé velmi pravděpodobně různý); součty použijte jako klíče asociativního pole v AWK a kontrolujte, jestli již v poli existují nebo ne. (do 30. 6.)pipeChat, který dostane jako parametr název místnosti a bude komunikovat s druhou instancí přes pojmenované roury /tmp/pipeChat.NAZEV.1 a /tmp/pipeChat.NAZEV.2. Pokud tyto soubory neexistují, vytvoří je (a při ukončení smaže) a bude používat jeden pro čtení a druhý pro zápis; pokud soubory již existují bude je využívat pro zápis a čtení v opačném pořadí. Po spuštění dvou instancí skriptu se stejným názvem místnosti se tedy text psaný do jedné z nich bude objevovat ve druhé a naopak. (do 30. 6.)man a seznamte se s příkazy, které budeme potřebovat (echo, ls, cd, mkdir, rmdir, touch, rm). Zobrazit řešeníman prikaz zobrazí manuálovou stránku k příkazu prikaz na aktuálním systému,man 1p prikaz na Linuxu zobrazí manuálovou stránku POSIXové verze příkazu prikaz.lsls -a / ls -A/etc. Zobrazit řešeníls -l /etca a přejděte do ní příkazem cd. Zobrazit řešenímkdir acd ab a b/c. Zobrazit řešenímkdir -p b/cb/--help, přejděte do složky b a soubor smažte. Zobrazit řešenítouch b/--helpcd brm -- --help (vše za parametrem -- se interpretuje jako soubor, bez něj by se vypsala nápověda)~/a) a příkazem touch vytvořte soubory textovy soubor.txt a *. Zobrazit řešenícd ..touch "textovy soubor.txt" "*" / touch textovy\ soubor.txt \** a ověřte příkazem ls, že nebylo smazáno nic jiného. Zobrazit řešenírm "*" (bez uvozovek by se hvezdička nahradila seznamem všech neskrytých souborů)ls.skryty.txt a vypište všechny soubory s příponou .txt. Zobrazit řešenítouch .skryty.txtls *.txt .*.txt (hvězdička se neexpanduje na skryté soubory)ls -a *.txt nefunguje, protože ls od shellu dostane celý seznam neskrytých souborů a -a už nic neovlivní)~/a). Zobrazit řešenírm -r * .*rmdir adresář a. Zobrazit řešenícd ..rmdir avi pomocí příkazu vimtutor.)cat obsah souboru /etc/passwd. Zobrazit řešenícat /etc/passwd / cat < /etc/passwdprvni.txt, který bude obsahovat text Toto je první soubor., použijte příkaz echo. Zobrazit řešeníecho "Toto je první soubor." > prvni.txtdruhy.txt, který bude obsahovat libovolný víceřádkový text, použijte příkaz cat. Zobrazit řešenícat << DomluvilJsem > druhy.txtprvní řádekdruhý řádekDomluvilJsemdruhy.txt na prvni.txt, čímž se tento smaže. Zkontrolujte obsah souboru prvni.txt. Zobrazit řešenímv druhy.txt prvni.txtcat prvni.txtdatum.txt s textem Datum a čas: a připište do něj příkazem date aktuální datum a čas. Zobrazit řešeníecho "Datum a čas:" > datum.txt (Vytváříme nový soubor / přepisujeme existující.)date >> datum.txt (Připisujeme na konec souboru.)less výstup příkazu getent passwd. Zobrazit řešenígetent passwd | lessless nedostal žádné parametry, bude tedy číst standardní vstup, kde najde výstup příkazu getent.getent passwd vrací na Linuxu obsah /etc/passwd rozšířený o záznamy z jiných zdrojů;whoami. Zobrazit řešenítouch "$(whoami)" (Bez uvozovek by se $(...) mohlo vyexpandovat na více parametrů touch.)touch "`whoami`"whoami a jeho výstup se vloží namísto $(...) nebo `...`.touch s novým parametrem.vi pomocí příkazu vimtutor. Zde aspoň první lekci, doporučuji ale projít později i zbytek.hello.sh, který vypíše příkazem echo text Hello world, nastavte mu potřebná práva příkazem chmod a spusťte jej jako ./hello.sh. Zobrazit řešenívim hello.sh a napíšeme do něj (po stisku i):#!/bin/bashecho "Hello world":wq soubor uložíme a zavřeme.chmod +x hello.sh../hello.sh.shellTest.sh, který vypíše text test expanze -{A,B}{C,D}-: -{A,B}{C,D}- v POSIXovém shellu a test expanze -{A,B}{C,D}-: -AC- -AD- -BC- -BD- v bashi. Vyzkoušejte za #! různé shelly: /bin/sh, /bin/bash, /bin/dash. Zobrazit řešeníshellTest.sh s obsahem:#!/bin/bashecho "test expanze -{A,B}{C,D}-:" -{A,B}{C,D}-chmod +x shellTest.sh./shellTest.sh, výstup:test expanze -{A,B}{C,D}-: -AC- -AD- -BC- -BD-#!/bin/dash a spustíme, výstup:test expanze -{A,B}{C,D}-: -{A,B}{C,D}-bash shellTest.sh nebo dash shellTest.sh,dash, o mnoho funkcí rozšířený bashsh, který se bude chovat jako jeden z nich.shbash i dash.favouriteShells.sh, který ze seznamu uživatelů passwd získá poslední sloupec s výchozími shelly a vypíše sestupně řazený seznam, kde na každém řádku bude počet výskytů shellu a cesta k němu -- např. 12 /bin/bash. Použijte příkazy sort, cut a uniq.) Zobrazit řešenígetent passwd | cut -d: -f7 | sort | uniq -c | sort -nr (jen pro Linux)cat /etc/passwd | cut -d: -f7 | sort | uniq -c | sort -nr (přenositelně)u-pl3 v Rotundě příkazem ssh u-pl3 ze školního počítače nebo příkazem ssh uzivatel@u-pl3.ms.mff.cuni.cz z vlastního. Připište do souboru /tmp/unix/users.txt své uživatelské jméno a volitelně za mezeru další informace (vše na stejný řádek). Podívejte se na ostatní jména v seznamu. Pošlete někomu zprávu příkazem write. Zůstaňte přihlášeni pro příjem zpráv od ostatních. Jste-li ke vzdálenému počítači přihlášeni ve více oknech můžete v některých vypnout příjem zpráv příkazem mesg n. Pro průběžné sledování změn v souboru users.txt lze využít příkaz watch./tmp/unix/users.txt s několika fiktivními uživatelskými jmény. Vytvořte skript broadcast.sh, který si nejprve uloží vše ze standardního vstupu do proměnné a poté pro každý řádek souboru /tmp/unix/users.txt vypíše obsah proměnné a na další řádek text write uzivatel, kde uzivatel je jméno uživatele ze souboru. Skript dobře otestujte. Mj. je potřeba, aby ignoroval veškeré další informace za uživatelským jménem až do konce řádku. Zobrazit řešeníbroadcast.sh:#!/bin/bashvstup="$(cat)"while read uzivatel zbytek; do (proměnnou $zbytek nepotřebujeme, ale zařídí pohlcení ostatních slov na řádku)echo "$vstup"echo write "$uzivatel"done < /tmp/unix/users.txt (přesměrování vstupu přímo u read by četlo stále 1. řádek)/tmp/unix/users.txt:novop Pepa Novotnyuser123echo "Zprava pro vsechny" | ./broadcast.sh:Zprava pro vsechnywrite novopZprava pro vsechnywrite user123write uzivatel a na jeho vstup mu předal text z proměnné. Skript by tedy měl poslat zadanou zprávu všem uživatelům v souboru /tmp/unix/users.txt. Vyzkoušejte jej na počítači u-pl3. Nepoužíváte-li školní počítače se sdílenou domovskou složkou, bude potřeba skript nejprve poslat na cílový počítač příkazem scp soubor uzivatel@u-pl3.ms.mff.cuni.cz:.. Zobrazit řešeníbroadcast.sh po drobné úpravě:#!/bin/bashvstup="$(cat)"while read uzivatel zbytek; doecho "$vstup" | (za znakem | může být zalomený řádek)write "$uzivatel"done < /tmp/unix/users.txtsubmatrix.sh, který dostane 5 parametrů: oddělovač, řádek1, sloupec1, řádek2, sloupec2; skript dostane na vstup tabulku s prvky na řádcích oddělenými daným oddělovačem (jako /etc/passwd pro :) a vypíše její výřez, tedy řádky řádek1--řádek2 obsahující sloupce sloupec1--sloupec2. Řádky i sloupce jsou počítány od 1 a intervaly jsou uzavřené. Použijte příkazy head, tail, cut; nepoužívejte cykly, vše lze vyřešit na jednom řádku. Zobrazit řešenísubmatrix.sh:#!/bin/bashhead -n"$4" | tail -n+"$2" | cut -d"$1" -f"$3-$5"submatrix.sh:#!/bin/bashoddelovac=$1radek1=$2sloupec1=$3radek2=$4sloupec2=$5head "-n$radek2" | (pošleme dál prvních $radek2 řádků)tail "-n+$radek1" | (pošleme dál vše od řádku $radek1)cut "-d$oddelovac" "-f$sloupec1-$sloupec2" (vybereme jen dané rozmezí sloupců)./submatrix.sh : 10 5 12 7 < /etc/passwdls -ltr | tr -s ' ' ' ' | ./submatrix.sh ' ' 2 6 11 100000catHeads.sh. Ten dostane jako parametry názvy souborů a pro každý má vypsat řádek --- nazev souboru ---, 20 úvodních řádků z jeho obsahu a jeden prázdný řádek. Pro vypsání začátku souboru využijte příkaz head. Zobrazit řešení#!/bin/bashfor soubor; do echo "--- $soubor ---" head -n20 "$soubor" echodonerandStr, která dostane jako první parametr množinu znaků (ve stejném formátu jako u příkazu tr), jako druhý parametr dostane počet znaků a vypíše náhodný textový řetězec dané délky složený ze znaků dané množiny. Použijte speciální soubor /dev/urandom, který obsahuje nekonečné množství náhodných dat; stačí je profiltrovat příkazem tr a vypsat pouze daný počáteční úsek příkazem head; za výstupem je vhodné ještě vypsat znak konce řádku. Zobrazit řešenírandStr() { tr -cd "$1" < /dev/urandom | head -c "$2"; echo; }randStr "0-9A-F" 4df sečte sloupec s volným místem na jednotlivých discích a vypíše výsledek v GiB. Spusťte jej na u-pl3 pro porovnání výsledků. Zobrazit řešenídf -k | {total=0while read a b c space rest; dototal=$((total + space))doneecho $((total / 1024 / 1024)) GiB}df -k | { total=0; while read a b c space rest; do total=$((total + space)); done; echo $((total / 1024 / 1024)) GiB; }| se provádí v subshellu,total=0df -k |while read a b c space rest; dototal=$((total + space))done (sečtení proběhlo v subshellu, který zde končí)echo $((total / 1024 / 1024)) GiB (tento příkaz opět vidí v $total nulu)^X^E (tedy Ctrl+x následované Ctrl+e);catHeads.sh z minula tak, aby vypisoval začátky pouze u textových souborů; u složek ať vypíše text Soubor je adresář. a u ostatních souborů Soubor je neznámého typu.. Pro větvení použijte konstrukci case ... in ... esac na výstup příkazu file, který typ souboru určí. Zobrazit řešenícatHeads.sh:#!/bin/bashfor soubor; docase "$(file --mime-type -b "$soubor")" intext/*)echo "--- $soubor ---"head -n20 "$soubor";;inode/directory)echo "Soubor je adresář.";;*)echo "Soubor je neznámého typu.";;esacechodonefile nezná přepínače--mime-type pro vypsání typu jako MIME ani -b pro neopisování názvu souborucase..h v adresáři /usr/share a zjistěte jejich počet. Zobrazit řešenífind /usr/share -name '*.h' 2>/dev/null | wc -l*.h musí být v uvozovkách,find,2> do /dev/null zakáže vypisování chyb o chybějících oprávněních,/etc (i rekurzivně). Zobrazit řešenífind /etc -type d 2>/dev/null | wc -l/usr/bin větší než 30MB. Zobrazit řešenífind /usr/bin -perm -ug+x -type f -size +$((1024*1024*30))c/var/log, které nebyly za posledních 14 dní změněny. Pro zjištění celkové velikosti přimějte příkaz find, aby jednou spustil příkaz du -csh soubory se všemi soubory splňujícími podmínku jako parametry. Zobrazit řešenífind /var/log -type f -mtime +13 -exec du -csh {} + 2>/dev/null | tail -n1 | cut -f1/usr/bin, které jsou novější než /bin/bash. Zobrazit řešenífind /usr/bin -type f -newer /bin/bash | wc -lgrep na vypsání řádků vstupu, které obsahují slovo s prvním písmenem velkým a ostatními malými. Otestujte, že příkaz funguje např. na vstupech Honza, TeX, vis, jaky je nelepsi editor?. Zobrazit řešení\| není v POSIXu):grep '\(^\|[^A-Za-z]\)[A-Z][a-z]*\([^A-Za-z]\|$\)'egrep '(^|[^A-Za-z])[A-Z][a-z]*([^A-Za-z]|$)'grep -E ... namísto egrep ....)sed každé číslo na vstupu třemi otazníky. Př. 100kc je asi 5 dolaru -> ???kc je asi ??? dolaru. Zobrazit řešenísed 's/[0-9][0-9]*/???/g'sed -r 's/[0-9]+/???/g'[0-9]* odpovídá i prázdnému řetězci,sed 's/[0-9]*/???/g'ab dostaneme ???a???b???.sed každé číslo na vstupu od konce po trojicích mezerami. Př. Bude to stat 12500kc. -> Bude to stat 12 500kc., muj telefon je +420123456789 -> muj telefon je +420 123 456 789. Zobrazit řešenísed ':begin; s/\([0-9]\)\([0-9]\{3\}\([^0-9]\|$\)\)/\1 \2/; t begin'^ a $ v závorkách nemusí dle POSIXu fungovat)sed -r ':begin; s/([0-9])([0-9]{3}([^0-9]|$))/\1 \2/; t begin'wget 'https://en.wikipedia.org/wiki/Linux' -O- | vim -, uložte soubor pod názvem linux.txt (bez zavření vimu) a proveďte v něm následující změny příkazy edu ve vimu: Odstraňte všechny řádky před řádkem začínajícím <p><b>Linux. Odstraňte všechny řádky počínaje řádkem obsahujícím pouze text </p>. Odstraňte všechny párové tagy sup včetně obsahu mezi nimi -- pro správné spárování otevíracího a ukončovacího tagu pomůže nejprve zalomit řádek za ukončovacími. Smažte všechny ostatní tagy, text mezi párovými tagy ponechte. Nakonec libovolnými prostředky vimu smažte závorku za úvodním slovem Linux a soubor uložte. Výsledkem by měl být soubor s úvodním textem z dané stránky Wikipedie bez číslovaných referencí a výslovnosti slova Linux, kde je každý odstavec na samostatném řádku; soubor by měl obsahovat 365 slov na 6-ti řádcích, ověřte příkazem wc. Zobrazit řešeníwget stahuje soubory z webu,-O mu určíme jako výstupní soubor -,-,:w linux.txtlinux.txt.:1,/^<p><b>Linux/-1 d:/^<\/p>$/,$ d</p>,^ a $.:%s=</sup>=&\r=gsup vložíme znak nového řádku.%,\n je ve vimu potřeba \r v textu k nahrazení;g umožní i opakované nahrazení v jednom řádku.:%s=<sup .*\n==sup až do konce řádku,\n.:%s=<[^>]*>==g>,ggwd%xgg se přesuneme na první řádek.w na následující slovo -- otevírací závorku.d smažeme vše až po místo,%, které přesune na párovou závorku.x.:x (ekvivalent :wq)sed na vypsání souboru linux.txt s každou větou na samostatném řádku. Každá věta začíná velkým písmenem a končí tečkou. Výstupem je 19 řádků. Zobrazit řešenísed 's/\(\.\) \([A-Z]\)/\1\n\2/g'sed -r 's/(\.) ([A-Z])/\1\n\2/g'sed na seřazení posloupnosti nul a jedniček na každém řádku -- nejprve vypište všechny nuly, pak všechny jedničky. Př. 00110100 -> 00000111. Zobrazit řešenísed ':begin; s/10/01/; t begin'grep na vypsání řádků, které lze rozdělit na dva stejné podřetězce. Zobrazit řešenígrep '^\(.*\)\1$'egrep '^(.*)\1$' (grep -E ... pro POSIXové řešení)emailsFromWeb.sh, který dostane jako parametr webovou adresu a vypíše všechny na ní uvedené emailové adresy v abecedním pořadí bez duplicit.) Zobrazit řešení#!/bin/bashwget "$1" -O- 2>/dev/null |egrep -o '[-a-zA-Z.0-9]+@[-a-zA-Z.0-9]+' |sort | uniqavg.sh, který dostane na každém řádku vstupu jedno číslo a má vypsat jejich aritmetický průměr s přesností na dvě desetinná místa. Použijte příkaz bc. Zobrazit řešení#!/bin/bashcnt=0sum=0while read number; docnt=$((cnt+1))sum=$(bc -q <<< "scale=2; $sum + $number")donebc -q <<< "scale=2; $sum / $cnt"sleep) a při ukončení vypíše text konec, k ukončení může dojít i pomocí signálů. Vyzkoušejte ukončení pomoci Ctrl+C i pomocí příkazu kill. Zobrazit řešení#!/bin/bashecho $$trap "echo konec" EXITsleep 10queue1.sh, který bude bude zpracovávat vstup po řádcích následovně: neprázdné řádky bude přidávat do fronty a při výskytu prázdného naopak jeden řádek z fronty odebere a vypíše. Využijte toho, že skript nepožaduje parametry a ukládejte obsah fronty v $1, $2, $3, ... pomocí set -- "prvni prvek" "druhy prvek" ... a shift. Zobrazit řešení#!/bin/bashset --while read line; doif [ -z "$line" ]; thenecho $1; shiftelseset -- "$@" "$line"fidonequeue2.sh, který bude obsahovat funkce enqueue na přidání řádku do fronty a dequeue na odebrání řádku a bude fungovat stejně jako předchozí; obsah fronty bude tentokrát uchovávat v běžné proměnné pomocí konstrukcí ${var#pattern}, ${var##pattern}, ${var%pattern}, ${var%%pattern}. Můžete předpokládat, že se na jednotlivých řádcích nevyskytují mezery. Zobrazit řešení#!/bin/bashqueue=""enqueue() {queue="$queue$* "}dequeue() {elem="${queue%% *}"queue="${queue#* }"}while read line; doif [ -z "$line" ]; thendequeueecho $elemelseenqueue $linefidonetimeGuard.sh, který každých 15 sekund vypíše řádek se svým prvním argumentem a s aktuálním časem. Namísto ukončování vimu používejte Ctrl+Z pro jeho pozastavení a příkaz fg pro obnovení. Spusťte skript s parametrem A a zkuste jej také pozastavit. Zjistěte příkazem jobs čísla a stav spuštěných úloh (vimu a skriptu) a přepínejte se mezi nimi. Obnovte běh skriptu na pozadí příkazem bg, spusťte skript na pozadí vícekrát pomocí & s různými parametry. Zkuste některé z instancí ukončit příkazem kill s použitím čísla úlohy z jobs, jiné pomocí Ctrl+C po přenesení do popředí. Zobrazit řešení#!/bin/bashwhile true; doecho "$1 " `date`sleep 15done^Z (Ctrl+Z)jobsfg, fg %3 (foreground)bg, bg %3 (background)kill %3&.filterLines.sh, který dostane jako parametry název vstupního a výstupního souboru. Skript bude postupně vypisovat řádky vstupního souboru a po každém bude čekat na stisk jedné klávesy; bude-li to y, připíše řádek do výstupního souboru, jinak jej zahodí. Pokud výstupní soubor před spuštěním skriptu již existoval, přepište jej. (Vedle standardního vstupu si otevřete ještě jeden vstup pro čtení souboru.) Zobrazit řešení#!/bin/bashif [ $# -ne 2 ]; thenecho "$0 INPUT_FILE OUTPUT_FILE"exitfiwhile read line <&3; do (řádky čteme z 3. vstupu)read -sn1 -p "$line" (zde používáme standardní vstup a výstup)echoif [ "$REPLY" == y ]; thenecho "$line" >&4 (řádky vypisujeme do 4. výstupu)fidone 3<"$1" 4>"$2" (jako 3. vstup otevřeme soubor $1 a jako 4. výstup $2)/dev/tty.delayedSend.sh, který dostane jako parametr název souboru, bude vyčkávat na vytvoření daného souboru a poté pošle jeho obsah na email uvedený v proměnné prostředí LOGMAIL. Využijte toho, že cat vrací nenulový návratový kód při neexistenci souboru. Před spuštěním skriptu si nastavte proměnnou LOGMAIL na svůj email příkazem export. Zobrazit řešení#!/bin/bash[ -n "$LOGMAIL" ] || exit 1until cat "$1" 2>/dev/null; dosleep 5;done |mail -s "[unix][script] file $1" "$LOGMAIL"nohup. Zkontrolujte příkazem ps, že skript stále běží. Vytvořte příslušný soubor s nějakým obsahem, ověřte, že byl skript ukončen a že Vám přišel email. Zobrazit řešeníexport LOGMAIL=email@example.comLOGMAIL se předá jako proměnná prostředí každému příkazunohup ./delayedSend.sh file.txt &ps x (Ověříme že běží.)echo test >> file.txtps x (Ověříme že byl ukončen.)tmux (terminal multiplexer), která umožňuje práci s více terminály v jednom okně a odpojení od aktuální relace bez ukončení běžících aplikací (např. při vzdálené práci přes SSH). Zobrazit řešenítmuxtmux a (attach)^B d (Ctrl+B, pak d jako detach)^B "^B %^B následované šipkami^B :set synchronize-panesawk '{ print $1, $NF }'usersLogins.awk, který pro vstup ve formátu souboru passwd vypíše řádky ve tvaru Uzivatel JMENO_UZIVATELE ma login LOGIN.. Výstup ať obsahuje pouze uživatele, v jejichž jméně každé slovo začíná velkým písmenem. Vyzkoušejte na výstup getent passwd a ověřte nepřítomnost úvodních uživatelů root, bin, daemon, ... Zobrazit řešení#!/usr/bin/awk -fBEGIN { FS=":" }$5 ~ /^([A-Z][a-z]*\s*)+$/ {print "Uzivatel " $5 " ma login " $1 "."}getent passwd | ./usersLogins.awk | headtodayFeaturedArticle.sh, který vypíše z úvodní stránky české Wikipedie https://cs.wikipedia.org/wiki/Hlavní_strana text sekce Článek týdne. Tato sekce je ohraničena tagy <div id="mp-tfa"> a </div>, ale obsahuje i vnořené tagy div, takže je potřeba počítat aktuální úroveň zanoření. Pomůže nastavení RS na < a FS na >. Vypisujte pouze text mezi jednotlivými tagy. Ke stažení stránky použijte wget. Zobrazit řešení#!/bin/bashwget 'https://cs.wikipedia.org/wiki/Hlavní_strana' -O- 2>/dev/null |awk -v RS="<" -v FS=">" -v ORS="" '/^div id="mp-tfa"/ { depth=0 }depth=="" { next }/^div/ { depth++ }/^\/div/ { depth-- }depth<=0 { print "\n"; exit }{ print $2 }'gsub. Zobrazit řešení#!/usr/bin/awk -fBEGIN { RS="," }{ gsub(/\s+/, "_"); print }wc.lsOwner.sh využívající AWK, který bude brát jako parametry názvy souborů/složek stejně jako ls a pro soubory, které by ls s danými parametry vypsal, vypíše řádky ve formátu JMENO_UZIVATELE NAZEV_SOUBORU s oběma sloupci zarovnanými. Jménem uživatele je zde myšleno jeho skutečné jméno, ne login. V části BEGIN si pomocí cyklu s getline načtěte výstup příkazu getent passwd a uložte si jména uživatelů podle loginů do asociativního pole; bude užitečné na tuto část skriptu změnit proměnnou FS. Zobrazit řešení#!/bin/bashls -l "$@" |awk 'BEGIN {FS=":"while ("getent passwd" | getline == 1) {users[$1]=$5}FS=" "}$9 { printf "%-25s %s\n", users[$3], $9 }'which, který bude hledat spustitelný soubor s daným názvem ve složkách uvedených v $PATH. Pro snadné procházení cest v $PATH nastavte IFS=:. Zobrazit řešení#!/bin/bashIFS=:for name; dofor dir in $PATH; doif [ -x "$dir/$name" ]; thenecho "$dir/$name";break;fidonedoneIFS dále se bude text po expanzi dělit na slova dvojtečkamiln vytvořte symlink směřujicí na tento soubor. Změňte obsah jednoho z nich a zkontrolujte obsah druhého. Smažte prvně vytvořený soubor, co se stalo s druhým? Zkuste totéž pomocí hardlinku. Zkuste si také zjistit počet hardlinků vedoucích na tyto soubory příkazy ls nebo stat. Zobrazit řešeníecho text > prvni.txtln -s prvni.txt druhy.txt (bez -s pro hardlink)echo druhy radek >> druhy.txtcat prvni.txtls -lrm prvni.txtcat druhy.txtmkfifo. V jednom okně použijte příkaz cat na zápis do této roury a ve druhém na čtení, poté něco napište do prvního okna a uzavřete vstup. Zobrazit řešenímkfifo mypipecat mypipecat > mypipenejaky text^DlockTest.sh, který po spuštění vypíše text locking by PID, kde PID je číslo procesu $$, vytvoří adresář /tmp/mylock jako zámek, vypíše text locked by PID, vyčká 1s a při ukončování (i pomocí signálu) vypíše text unlocking by PID a složku smaže. Spusťte paralelně 10 instancí tohoto skriptu (for i in `seq 10`; do ./lockTest.sh & done) a ověřte, že zámek vždy vlastní jen jedna z nich. Zobrazit řešení#!/bin/bashecho "locking by $$"while ! mkdir /tmp/mylock 2>/dev/null; dosleep 0.1donetrap "echo unlocking by $$; rmdir /tmp/mylock" EXITecho locked by $$sleep 1locking...,locked...,unlocked... a další vypíše locked..., ...getopts. Napište skript hello.sh, který bude mezi parametry přijímat přepínač -e pro angličtinu, -c pro češtinu (příp. jiné jazyky) a -n pro určení jména -- tento přepínač tedy obsahuje jméno jako parametr. Skript vypíše pozdrav v daném jazyce vč. příp. oslovení (bylo-li zadáno jméno). V případě zadání více vzájemně se vylučujících přepínačů (jazyků) se použije poslední z nich. Zobrazit řešení#!/bin/bashczech=falsename=""while getopts cen: opt; docase $opt inc) czech=true;;e) czech=false;;n) name=$OPTARG;;esacdoneif $czech; thenecho -n Ahojelseecho -n Hellofiif [ -n "$name" ]; thenecho ", $name!"elseecho "!"fieditLines, který bude přidávat a mazat řádky zadaného souboru. Prvním parametrem bude název souboru k editaci, druhým název operace insert/delete, třetím regulární výraz určující řádek, za nějž se mají připisovat řádky ze vstupu nebo který má být smazán. Zobrazit řešení#!/bin/bash[ $# = 3 ] || exit 1{echo "/$3/"case "$2" ininsert)echo awhile IFS='' read line; doif [ "$line" != "." ]; thenecho "$line"elseecho -e "..\n.\ns/.//\na"fidoneecho .;;delete)echo d;;esacecho -e "w\nq"} | ed "$1" >/dev/nullmailReader, který zpracuje email ze svého vstupu. Skript ze zprávy přečte adresu odesílatele (bez zobrazovaného jména) a předmět; do souboru pojmenovaném po adrese odesílatele poté připíše řádek === předmět === s předmětem zprávy následovaný textem zprávy. Příklady zpráv najdete ve složce u-pl3:/tmp/unix/mails/. Zobrazit řešení#!/bin/bashwhile IFS=: read name value; docase "$name" inFrom) from=`sed -r 's/.*<(.*)>/\1/; s/^\s*|\s*$//g' <<< "$value"`;;Subject) subj=`sed -r 's/^\s*|\s*$//g' <<< "$value"`;;'') break;;esacdone[ -n "$from" ] || exit 1{echo "=== $subj ==="catecho} >> "$from"users a v něm adresáře pro prvních 20 uživatelů pojmenované jejich uživatelskými jmény; použijte k tomu xargs. Zobrazit řešenímkdir userscd usersgetent passwd | head -n20 | cut -d: -f1 | xargs mkdir