# Úvod do UNIXu (SWI095x1h), 5. cvičení, 2005-03-23
# SU1, Malá Strana, MFF UK
#
# Řešení jsou odladěná na FreeBSD 4.11-RELEASE, konkrétní příklady
# na zpracování /etc/passwd se týkají stroje mail.kolej.
#
# $DevNullCZ: cviceni-05.reseni.txt,v 1.2 2005/04/01 08:43:48 jp Exp $
#

#---------------------------------------------------------------------
# Regulární výrazy
#
# Dobře vysvětlené v manuálové stránce pro grep(1).
#---------------------------------------------------------------------

NOTE: "extended" regular expressions vers. "basic" regular
expressions - tedy rozšířené RE (RE = běžně používaná zkratka pro
"Regular Expression") proti základním RE. Rozdíl je v syntaxi a
kromě toho, v někerých programech pomocí rozšířených RE dokážete
více než pomocí základních, v jiných programech se tyto dva způsoby
liší pouze tou syntaxí. Druhým případem je i GNU grep, je tedy
jedno, jaký zápis regulárních výrazů budete používat. Ale je nutné
si uvědomit, že grep(1) používá defaultně základní RE (pro rozšířené
je třeba použít option "-E"), egrep(1) naopak používá defaultně
rozšířený zápis. Rozdíl mezi těmito dvěmi způsoby zápisu je v
použití řídících znaků, viz man grep(1):

# "In  basic  regular  expressions the metacharacters ?, +, {, |, (,
# and ) lose their special meaning; instead use the  backslashed
# versions  \?, \+, \{, \|, \(, and \)."

Tedy platí toto:

# echo aaa | grep 'a\{3\}'
# aaa
# echo aaa | grep 'a{3}'
# echo aaa | egrep 'a\{3\}'
# echo aaa | egrep 'a{3}'
# aaa

(g2) Vypište řádky, kde uživatelské jméno je tvaru "xxxxNNNN" a uid
je "1NNNN". "x" je libovolné písmeno (nemusí být všechna stejná),
"N" je libovolné číslo (nemusi všechna být stejná). Například login
"jpec4332" s UID "14332" danou podmínku splňuje, login "jmag9999" s
UID "13333" nesplňuje.


(g3) Vypište řádky, které mají login "xxxxNNNN", ale nesplňují
podmínku z předchozího příkladu. Výsledek je pouze jeden řádek.


(g4) Najděte uživatele, kteří mají login name tvaru "xyyyNNNN" a
jejich jméno je "x... yyy..." Ignorujte rozdíly ve velkých/malých
písmenech. (příklad takového uživatele by byl třeba user "jrya1234"
se jménem "Jack Ryan"). Výsledek by měl být 97.


(g5) Jako (g4), ale pro přeskočení sloupců mezi loginem a jménem
uživatele použijte počet opakování (pokud jste to již nepoužili v
předchozím příkladu).

NOTE: pozor na to, že označení sloupce jako ":.*" není dobře,
protože takový regulární výraz označí i více sloupců najednou (tečka
je jakýkoli znak, tedy i znak ':'). Je proto nutné použít ":[^:]*".


(g6) Nyní vypište i uživatele typu "Ivo Kristián Kubák". Mělo by vám
vyjít 98. Všimněte si, jak je tento příkaz ve srovnání s předchozím
pomalý - to způsobuje to volitelné prostřední jméno, což nutí grep
se zpětně vracet v backtrackingu.


(g7) v souboru "/home/pechanec/rfc/rfc793.txt" spočítejte, kolik
řádků obsahuje slovo "packet" přesně v tomto tvaru, bez uvozovek.
Nezajímají mě tedy řádky například se slovem "packets". Výsledek má
vyjít 15.

Pokud vám na konci zbyde čas, nebo doma, zkuste najít více řešení.


(g8) ve stejném souboru spočítejte řádky, které obsahují slovo
"packet" NEBO slovo "network", přesně v těchto tvarech. Výsledek je
48.


(g9) Spočtěte, kolik je v daném RFC dokumentu řádků s řetězci,
které jsou alespoň 3 znaky dlouhé a mohou obsahovat pouze levou
hranatou závorku, pravou hranatou, nebo číslice. Jde mi o jakoukoli
směs vytvořenou z těchto znaků, tedy třeba "[1]", "[[[", "222",
"]22" atd... Výsledek je 238.


(g10) Spočítejte řádky, které obsahují řetězce číslic, které jsou
alespoň délky 3 a ve kterých jsou všechny číslice STEJNÉ. Tedy "222"
je ten správný řetězec, "2223" také, ale "22445566" ne. Výsledek je
6.


(g11) Na závěr něco těžšího - nalezněte RE, který vybere ze
souboru "/home/pechanec/public/ip_addresses.txt" platné IP adresy.
Pro přístup pouze z webu, soubor má kdyžtak tento obsah (samozřejmě
bez úvodních znaků "# ", které jsou zde jen pro zvýraznění textu):

# 1.2.3.4
# 1.2.3.999
# 999.999.999
# 255.255.255
# 1234.3.3.3
# 195.250.128.34
# 195.250.999.34
# 192.168.07.200
# 10.0.0.1
# 10.0.0.1.1
# 195.256.128.1
# 10.0.0 1.1
# 10.0.256.1
# 0.0.0.0
# 1.2.3.4.

Správný výsledek vypadá takto:

# 1.2.3.4
# 195.250.128.34
# 10.0.0.1
# 0.0.0.0

Pokud bude výsledek vypadat takto:

# 1.2.3.4
# 195.250.128.34
# 192.168.07.200
# 10.0.0.1
# 0.0.0.0

beru to také, i když zápis adresy "192.168.07.200" není korektní
kvůli třetímu bajtu ("07"). V mém řešení totiž bude jen takový
zápis, který poskytne ten druhý výpis, tj. včetně nekorektní adresy
"192.168.07.200"; pro opravdu korektní řešení bych musel příslušný
RE výrazně zesložitit, do čehož se mi opravdu už nechtělo. Pokud
někdo najde rozumné řešení, které vypíše dané 4 řádky, určitě mi ho
pošlete !!!



(g12) Pokud máte chuť, vyřešte si příklad (g10) pomocí fgrep(1). "f"
zde neznamená "fast", ale "fixed" - tedy tato forma grepu akceptuje
pouze fixní řetězce. Jak má vypadat formát takového regulárního
výrazu se podívejte do manu, a najděte si option "-F". Až to budete
mít, tak si změřte, jaký je rozdíl mezi časem běhu tohoto řešení a
předchozího. Řešení s fgrep je výrazně rychlejší.

Čas vykonání příkazu se změří tak, že původní příkaz se dá jako
parametr příkazu "time", tedy např.:

# [KOLEJ-MAIL:pechanec]~/rfc$ time ls                    
# rfc791.txt  rfc793.txt
# 
# real    0m0.006s
# user    0m0.004s
# sys     0m0.001s


(gxx) Místo posledního příkladu jen poznámka, že REGULÁRNÍ VÝRAZY
JSOU EXTRÉMNĚ DŮLEŽITÉ, A ROZHODNĚ SE VYPLATÍ NAD NIMI STRÁVIT
NĚJAKÝ ČAS. Pokud máte zájem, vřele doporučuju tento odkaz:

http://www.regular-expressions.info/