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
.ls
ls -a
/ ls -A
/etc
. Zobrazit řešeníls -l /etc
a
a přejděte do ní příkazem cd
. Zobrazit řešenímkdir a
cd a
b
a b/c
. Zobrazit řešenímkdir -p b/c
b/--help
, přejděte do složky b
a soubor smažte. Zobrazit řešenítouch b/--help
cd b
rm -- --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.txt
ls *.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 a
vi
pomocí příkazu vimtutor
.)cat
obsah souboru /etc/passwd
. Zobrazit řešenícat /etc/passwd
/ cat < /etc/passwd
prvni.txt
, který bude obsahovat text Toto je první soubor.
, použijte příkaz echo
. Zobrazit řešeníecho "Toto je první soubor." > prvni.txt
druhy.txt
, který bude obsahovat libovolný víceřádkový text, použijte příkaz cat
. Zobrazit řešenícat << DomluvilJsem > druhy.txt
první řádek
druhý řádek
DomluvilJsem
druhy.txt
na prvni.txt
, čímž se tento smaže. Zkontrolujte obsah souboru prvni.txt
. Zobrazit řešenímv druhy.txt prvni.txt
cat prvni.txt
datum.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 | less
less
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/bash
echo "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/bash
echo "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ý bash
sh
, který se bude chovat jako jeden z nich.sh
bash
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/bash
vstup="$(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 Novotny
user123
echo "Zprava pro vsechny" | ./broadcast.sh
:Zprava pro vsechny
write novop
Zprava pro vsechny
write user123
write 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/bash
vstup="$(cat)"
while read uzivatel zbytek; do
echo "$vstup" |
(za znakem |
může být zalomený řádek)write "$uzivatel"
done < /tmp/unix/users.txt
submatrix.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/bash
head -n"$4" | tail -n+"$2" | cut -d"$1" -f"$3-$5"
submatrix.sh
:#!/bin/bash
oddelovac=$1
radek1=$2
sloupec1=$3
radek2=$4
sloupec2=$5
head "-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/passwd
ls -ltr | tr -s ' ' ' ' | ./submatrix.sh ' ' 2 6 11 100000
catHeads.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/bash
for soubor; do
echo "--- $soubor ---"
head -n20 "$soubor"
echo
done
randStr
, 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" 4
df
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=0
while read a b c space rest; do
total=$((total + space))
done
echo $((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=0
df -k |
while read a b c space rest; do
total=$((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/bash
for soubor; do
case "$(file --mime-type -b "$soubor")" in
text/*)
echo "--- $soubor ---"
head -n20 "$soubor";;
inode/directory)
echo "Soubor je adresář.";;
*)
echo "Soubor je neznámého typu.";;
esac
echo
done
file
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 -l
grep
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.txt
linux.txt
.:1,/^<p><b>Linux/-1 d
:/^<\/p>$/,$ d
</p>
,^
a $
.:%s=</sup>=&\r=g
sup
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%x
gg
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/bash
wget "$1" -O- 2>/dev/null |
egrep -o '[-a-zA-Z.0-9]+@[-a-zA-Z.0-9]+' |
sort | uniq
avg.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/bash
cnt=0
sum=0
while read number; do
cnt=$((cnt+1))
sum=$(bc -q <<< "scale=2; $sum + $number")
done
bc -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/bash
echo $$
trap "echo konec" EXIT
sleep 10
queue1.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/bash
set --
while read line; do
if [ -z "$line" ]; then
echo $1; shift
else
set -- "$@" "$line"
fi
done
queue2.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/bash
queue=""
enqueue() {
queue="$queue$* "
}
dequeue() {
elem="${queue%% *}"
queue="${queue#* }"
}
while read line; do
if [ -z "$line" ]; then
dequeue
echo $elem
else
enqueue $line
fi
done
timeGuard.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/bash
while true; do
echo "$1 " `date`
sleep 15
done
^Z
(Ctrl+Z)jobs
fg
, 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/bash
if [ $# -ne 2 ]; then
echo "$0 INPUT_FILE OUTPUT_FILE"
exit
fi
while read line <&3; do
(řádky čteme z 3. vstupu)read -sn1 -p "$line"
(zde používáme standardní vstup a výstup)echo
if [ "$REPLY" == y ]; then
echo "$line" >&4
(řádky vypisujeme do 4. výstupu)fi
done 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 1
until cat "$1" 2>/dev/null; do
sleep 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.com
LOGMAIL
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.txt
ps 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ítmux
tmux a
(attach)^B d
(Ctrl+B, pak d jako detach)^B "
^B %
^B
následované šipkami^B :set synchronize-panes
awk '{ 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 -f
BEGIN { FS=":" }
$5 ~ /^([A-Z][a-z]*\s*)+$/ {print "Uzivatel " $5 " ma login " $1 "."}
getent passwd | ./usersLogins.awk | head
todayFeaturedArticle.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/bash
wget '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 -f
BEGIN { 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/bash
ls -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/bash
IFS=:
for name; do
for dir in $PATH; do
if [ -x "$dir/$name" ]; then
echo "$dir/$name";
break;
fi
done
done
IFS
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.txt
ln -s prvni.txt druhy.txt
(bez -s
pro hardlink)echo druhy radek >> druhy.txt
cat prvni.txt
ls -l
rm prvni.txt
cat druhy.txt
mkfifo
. 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 mypipe
cat mypipe
cat > mypipe
nejaky text
^D
lockTest.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/bash
echo "locking by $$"
while ! mkdir /tmp/mylock 2>/dev/null; do
sleep 0.1
done
trap "echo unlocking by $$; rmdir /tmp/mylock" EXIT
echo locked by $$
sleep 1
locking...
,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/bash
czech=false
name=""
while getopts cen: opt; do
case $opt in
c) czech=true;;
e) czech=false;;
n) name=$OPTARG;;
esac
done
if $czech; then
echo -n Ahoj
else
echo -n Hello
fi
if [ -n "$name" ]; then
echo ", $name!"
else
echo "!"
fi
editLines
, 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" in
insert)
echo a
while IFS='' read line; do
if [ "$line" != "." ]; then
echo "$line"
else
echo -e "..\n.\ns/.//\na"
fi
done
echo .;;
delete)
echo d;;
esac
echo -e "w\nq"
} | ed "$1" >/dev/null
mailReader
, 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/bash
while IFS=: read name value; do
case "$name" in
From) from=`sed -r 's/.*<(.*)>/\1/; s/^\s*|\s*$//g' <<< "$value"`;;
Subject) subj=`sed -r 's/^\s*|\s*$//g' <<< "$value"`;;
'') break;;
esac
done
[ -n "$from" ] || exit 1
{
echo "=== $subj ==="
cat
echo
} >> "$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 users
cd users
getent passwd | head -n20 | cut -d: -f1 | xargs mkdir