Žolíkové znaky a regulární výrazy

Motto:
Zeptejte se jakéhokoliv relativně zkušeného *NIX uživatele na 10 nejlepších věcí okolo operačního systému, a téměř vždy v jeho mumlání, někde mezi "99% spolehlivostí" a "vzdáleným rebootováním systému", uslyšíte výraz "REGULÁRNÍ VÝRAZY".

Zeptejte se jakéhokoliv relativně zkušeného *NIX uživatele na 10 nejprotivnějších věcí okolo operačního systému, a někde mezi "zombie procesy" a "instalací" uslyšíte výraz "REGULÁRNÍ VÝRAZY".

P.Satrapa: Unix bez regulárních výrazů je jako sex bez partnera/partnerky. Dá se to používat, ale člověk o cosi zásadního přichází.

Regulárni výrazy (regex) stručně :

Jejich prostřednictvím můžete:

Metaznaky

Meta-
znak
Jméno
operátoru
splňujePříklad regulárního výrazu
.
tečka libovolný jeden znak s výjimkou NUL. r.t budou splňovat řetězce rat, rut, r t, ale ne root (dvě o) ani Rot v Rotava (velké R).
*
hvězdička nula nebo více výskytů bezprostředně předcházejícího znaku. .* znamená jakýkoliv počet jakýchkoliv znaků.
$
ukotvení znakem dolar konec řetězce/řádky děj$ bude splňovat konec řetězce "Je to zloděj" ale ne řetězec "také dějiny ukázaly" nebo "mělo to pěkný děj."
Pokud je operátor $ poslední operátor reg. výrazu nebo je bezprostředně za pravou kulatou závorkou, musí mu předcházet zpětné lomítko.
^
ukotvení znakem šipka hore (stříška) začátek řetězce/řádky. ^pokud se bude splňovat začátek řetězce "pokud se to povede" ale nebude splňovat "nevím, pokud se mi" .
[ ] 
[c1-c2]
[^c1-c2]
hranaté závorky libovolný ze znaků mezi závorkami. r[aou]t splňuje rat, rot, a rut, ale ne ret. Rozsah znaků může být specifikován užitím pomlčky (mínus). Například regulární výraz [0-9] znamená splnění jakoukoliv číslicí. Může být specifikováno i více rozsahů. Regulární výraz [A-Za-z] znamená splnění jakýmkoliv malým nebo velkým ASCII písmenem. Ke splnění jakéhokoliv znaku s výjimkou těch v rozsahu (doplněk rozsahu)je nutno použít znak stříěka jako první znak po otevírací závorce. Např. výraz [^269A-Z] splňují libovolné znaky mimo 2, 6, 9, a velkých ASCII písmen.
Pozn.: Uvnitř hranatých závorek možno též použít předdefinované pojmenované třídy znaků [:alnum:], [:alpha:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:] a [:xdigit:]
[^c1-c2]
stříška uvnitř hranatých závorek doplněk rozsahu -- libovolný znak mimo těch vyjmenovaných v rozsahu který následuje stříšku jako první znak po otevírací závorce. [^269A-Z] budou splňovat jakékoliv znaky s výjimkou 2, 6, 9, a velkých ASCII písmen.

Pokud je operátor ^ první operátor regulárního výrazu nebo první znak uvnitř závorek, musí mu předcházet zpětné lomítko.

\
zpětné lomítko Je to uvozovací znak, používá se k označení že následující znak se má brát jako obyčejný znak. Např. \$ je užit ke shodě se znakem dolar ($) místo s koncem řádku. Podobně výraz \. je užit ke shodě s tečkou místo s libovolným znakem.

Operátorům uvnitř hranatých závorek nemusí předcházet zpětné lomítko.

\< \>
levá a pravá úhlová závorka, menší a větší začátek (\<) nebo konec (\>) slova nebo slovo. \<kus splňuje "kus" v řetězci "To je kus !" ale nesplňuje "kus" v "systém pokus-omyl". Pozn: tento metaznak není podporován všemi aplikacemi.
\( \)
levá a pravá kulatá závorka výraz mezi \( a \) jako skupina. Také uchovává znaky splňované výrazem do dočasné paměti. V jednom regulárním výrazu do ní může být uloženo až devět splňujících vzorků, které pak mohou být odkazovány jako \1\9.
|
roura (nebo, to nebo to) "OR" dvou podmínek. (jeho|jejich) splňuje řádka "Ani jeho to netěšilo" a také řádka "ale jejich to též nebylo" ale už ne řádka "a jim to půjčím." Pozn: tento metaznak není podporován všemi aplikacemi.
+
znaménko plus jeden nebo více výskytů bezprostředně předcházejícího znaku nebo regulárního výrazu. 9+ splňují 9, 99, nebo 999. Pozn: tento metaznak není podporován všemi aplikacemi.
?
otazník splňuje 0 nebo 1 výskyt bezprostředně předcházejícího znaku nebo regulárního výrazu. ? je ekvivalent s {0,1}.
\{i\}
\{i,j\}
složené závorky specifický počet nebo rozsah opakování předcházejícího znaku/regexp. A[0-9]\{3\} bude splňovat "A" následované přesně třemi číslicemi. Tzn. např. A123 ale ne A1234.
[0-9]\{4,6\} splňuje jakákoliv posloupnost 4, 5, nebo 6 číslic.

Jednoduché výrazy

- Nejjednodušším regulárním výrazem: obyčejné písmeno - třeba "r" . Když se v textu hledá řetězec, který by tomuto regulárnímu výrazu vyhověl, hledá se jednoduše písmeno "r".

- Řetězení: Použijete-li regulární výraz root, představuje vlastně zřetězení čtyř elementárních jednopísmenných regulárních výrazů. V textu se bude hledat slovo "root".

- znak tečka (.). Při hledání jí vyhoví libovolný znak kromě konce řádku. Nelze ji však ignorovat - nějaký znak jí program vždy musí přiřadit.

- skupina znaků v hranatých závorkách. Regulárnímu výrazu bude vyhovovat právě jeden z těchto znaků. Například výrazu [xyz] vyhoví buď znak "x" nebo "y" nebo "z". Interval: v hranatých závorkách uvedeme pouze jeho meze spojené pomlčkou. Jednotlivých znaků a jejich intervalů můžete do hranatých závorek napsat, co hrdlo ráčí. Například výrazu [a0-9zl-nt] vyhoví libovolné z písmen a, l, m, n, t, z nebo libovolná číslice.

Kromě pomlčky se v hranatých závorkách vyskytuje ještě jeden speciální znak. Pokud hned za otevírací hranatou závorkou zapíšete stříšku ( ^), bude celá skupina negována. To znamená, že regulárnímu výrazu vyhoví libovolný znak odlišný od těch, které jsou uvedeny ve skupině. Například [^0-9] vyhoví cokoli kromě číslice.

Intervaly znaků vycházejí z kódování ASCII. To znamená, že například výrazu [a-z] vyhoví libovolné malé písmeno anglické abecedy. Doplnit velká písmena není žádný velký problém ( [a-zA-Z]), pro práci s českými znaky viz následující odstavec.

Uvnitř hranatých závorek použít předdefinované třídy znaků: [:alnum:] = alfanumerický znak, [:alpha:] = písmeno, [:cntrl:] = řídící znak, [:digit:] = číslice, [:graph:] = tisknutelný znak mimo mezery, [:lower:] = malé písmeno, [:print:] = tisknutelný znak včetně mezery, [:punct:] = tisknutelný znak mimo mezery a alfanumerických znaků, [:space:] = mezera <TAB> <NL> <CR> <FF> <VTAB>, [:upper:] = velké písmeno, [:xdigit:] = [0123456789abcdefABCDEF].
Například [[:alnum:]] znamená [0-9A-Za-z] pokud máme locales nastavené na C a ASCII znakovou sadu, ale první množina znaků představuje číslice a písmena nezávisle na národním prostředí a znakové sadě - tzn. pokud máme nastavenou češtinu a znakovou sadu ISO-8859-2 ( např. LANG=cs_CZ.ISO-8859-2 ), pak do této množiny budou patřit i všechna písmena s diakritikou (např. znak s hodnotou 0xC1 == velké A s čárkou).

- Vyřazení speciálního významu některých znaků: v Unixu bývá zvykem, že pokud speciálnímu znaku předřadíte zpětné lomítko, vypnete tak jeho speciální chování (a v některých případech právě naopak :).

Uvnitř hranatých závorek panuje specifické prostředí. Tečka zde představuje obyčejnou tečku a význam ostatních dvou speciálních znaků lze potlačit prostým pořadím. Stříška představuje negaci jen pokud je uvedena na samotném začátku a pomlčka slouží jako oddělovač intervalu jen pokud má z obou stran jeho meze. Takže například výrazu [.^az-]vyhoví pouze jeden ze znaků ".", "^", "-", "a" nebo "z".

Pokud má být jedním z povolených znaků pravá hranatá závorka, uveďte ji hned za otevírací. Takže například regulárnímu výrazu [][] vyhoví levá nebo pravá hranatá závorka. Pokud byste znaky uvnitř vnějších hranatých závorek zapsali v opačném pořadí ( [[]]), význam by se radikálně změnil: byl by interpretován jako [[] bezprostředně následované ]. Čili vyhověl by mu jedině řetězec "[]".

Shrnutí

výraz vyhovuje
znak odpovídající znak
. libovolný znak
[znaky] jeden z uvedených znaků
[^znaky] libovolný znak kromě uvedených
\x vyřadí/zapne speciální význam znaku x

Opakování regulárních výrazů: hvězdička ( *). Znamená, že regulární výraz bezprostředně před ní se může zopakovat, kolikrát to jenom jde.

Příklad:
Výrazu A* tedy vyhoví libovolný počet písmen "A", zatímco [0-9]* ztělesňuje libovolně dlouhou posloupnost číslic (opakovaným regulárním výrazem je zde [0-9], tedy libovolná číslice).

V řadě programovacích jazyků je identifikátor definován jako libovolně dlouhá posloupnost písmen a číslic začínající písmenem. Pomocí regulárního výrazu bychom jej zapsali jako [a-zA-Z][a-zA-Z0-9]*. Zdůrazňuji, že opakování se týká jen regulárního výrazu, který je uveden bezprostředně před hvězdičkou. Uvedený výraz tedy znamená "právě jeden výskyt [a-zA-Z] (písmeno), za nímž následuje libovolný počet výskytů [a-zA-Z0-9] (písmeno nebo číslice)".

Snad nejběžnějším opakovaným výrazem je tečka, která v kombinaci s hvězdičkou ( .*) znamená "libovolný řetězec znaků". V souvislosti s opakováním si dobře zapamatujte tři důležité skutečnosti:

Přípustnost nulového počtu opakování znamená, že opakovanému regulárnímu výrazu vždy může vyhovět i prázdný řetězec. Praktickým důsledkem je, že jen vzácně dává smysl vyhledávat samotný opakovaný výraz. Zpravidla je třeba jej alespoň z jedné strany ohraničit něčím povinným.

Příklad:
Chcete-li vyhledat v textu všechna čísla, nemá smysl hledat "libovolně dlouhou posloupnost číslic" ( [0-9]*), protože posloupnost číslic nulové délky obsahuje každý řádek (vyzkoušejte grep '[0-9]*' soubor na libovolný soubor - uvidíte, že "najde" všechny jeho řádky). Správně je třeba hledat "alespoň jednu číslici", tedy použít regulární výraz [0-9][0-9]*.

Skutečnost, že opakování se týká regulárního výrazu, nikoli srovnávaného řetězce, je velmi důležitá. Zapíšete-li .*, spojujete dva prvky: symbol pro libovolný znak a symbol opakování. Výslednou konstrukci lze chápat dvěma způsoby. Buď jako libovolný počet libovolných znaků (opakování regulárního výrazu) nebo že v textu může být libovolný znak a ten se pak může opakovat, kolikrát chce (opakování ve zkoumaném řetězci). Správný je první výklad, jinak bychom se z toho nejspíš zbláznili.

Hladovost opakování se projevuje tím, že opakovaný regulární výraz se vždy snaží roztáhnout na co největší délku - zahrnout do sebe co největší počet znaků zkoumaného řetězce. Proto když například řetězec "brambora" srovnáte s regulárním výrazem r.*a (libovolný řetězec znaků začínající "r" a končící "a"), bude vyhovujícím řetězcem "rambora" (od prvního "r" až po poslední "a").

Regulární výrazy versus vzorky, žolíkové znaky ve vzorcích

Pro specifikaci souborů (např. v shellech sh, bash aj., v programu find) se používají tzv. vzorky (patterns). V těchto vzorcích se používají jednak normální znaky, které odpovídají samy sobě, a jednak speciální znaky (nazývané žolíkové /wildcards/ znaky), které mají zvláštní význam:
*
Splňuje jakýkoliv řetězec včetně prázdného
?
Splňuje jakýkoliv jeden znak
[...]
Splňuje jakýkoliv ze znaků v závorkách uzavřených. Dva znaky oddělené pomlčkou definují rozsah - bude jej splňovat jakýkoliv znak mezi těmito znaky včetně, za použití aktuální národní znakové sady a třídících pravidel. Pokud je první znak následující [ znak ! nebo ^ , pak tuto konstrukci splňuje jakýkoliv znak kromě uzavřených (negace).

Začátečníci někdy zaměňují regulární výrazy s žolíkovými znaky. Jistá podobnost tu skutečně je - oba prostředky umožňují vytvářet jakési vzory, které jsou porovnávány se skutečnými daty. Existují mezi nimi dva zásadní rozdíly. Žolíkové znaky se týkají názvů souborů a zpracovává je interpret příkazů (shell). Naproti tomu regulární výrazy se zaobírají obsahem (textových) souborů a jejich interpretaci mají na starosti jednotlivé programy (editory, grep a podobně).

Případným omylům ještě nahrává podobnost některých speciálních znaků mezi oběma konstrukcemi. V tomto směru je záhodno především mít na paměti, že zatímco v žolíkových znacích * představuje libovolný řetězec, v regulárních výrazech se libovolný řetězec zapisuje pomocí .*.

Mechanika srovnávání

Pojmem srovnávání (matching) se označuje proces, kdy program hledá, zda předložený řetězec znaků odpovídá regulárnímu výrazu či nikoli. Zároveň se program snaží stanovit, které části řetězce odpovídají jednotlivým částem regulárního výrazu.

Dokud se zabýváte pouze hledáním, je pro vás v podstatě nezajímavé, kde přesně byl daný vzor nalezen. Ovšem když používáte regulární výrazy k nahrazování (a právě v tom je jejich největší síla), je tato informace velmi důležitá.

Základní princip srovnávání je následující: začne se od začátku řetězce. Každému prvku regulárního výrazu se snaží přiřadit vždy co nejdelší posloupnost znaků ze zkoumaného textu a teprve pak pokračuje srovnáváním dalších částí. Pokud to později nevyjde, vrátí se zpět a zkusí přidělený řetězec o jeden znak zkrátit. Jestliže již zkrátil na minimum vše, co zkrátit šlo, a přesto se nepodařilo najít shodu, posune se na další znak zkoumaného textu a vše se rozjede znovu.

Příliš hladové opakování

Na hladovost opakování je třeba si dávat pozor. Díky ní se snadno může stát, že opakující se výraz pohltí i ty znaky, se kterými jste nepočítali. Klasickým příkladem tohoto chování je regulární výraz pro řetězec znaků v uvozovkách.


Začátečníci mají často tendenci uvažovat následovně: řetězec v uvozovkách, to jsou otevírací uvozovky, pak cokoli a na konci druhé uvozovky. To vyjádříme regulárním výrazem ".*". Problém je, že díky hladovosti hvězdičky se tento regulární výraz roztáhne od prvních uvozovek ve zkoumaném řetězci až po poslední.

Řešením je nepřipouštět v uzavřeném řetězci libovolné znaky, ale pouze znaky jiné než koncový. V našem případě cokoli kromě uvozovek, takže tím správným regulárním výrazem bude "[^"]*":

Mimochodem - kdybyste na řetězec aplikovali výraz .*".*", roztáhne se první .* na 'Volali "Ahój" a ' a druhé .* na 'Nazdár'. Jelikož jsou v regulárním výrazu povinné uvozovky, nemůže první .* schramstnout všechno. Ukousne si však co nejvíc, čímž druhé .* omezí na minimum.

Omezený počet opakování

Základním problémem klasické opakovací hvězdičky je, že je nekontrolovatelná. Pro některé situace potřebujete přesnější vyjadřování.

Vaše touhy uspokojí konstrukce \{min,max\} . Opět se vztahuje na bezprostředně předcházející regulární výraz a říká, že se má opakovat alespoň min-krát, nanejvýš však max-krát. Jako každé opakování i tohle je hladové, takže se snaží uplatnit vždy co největší z povoleného počtu opakování.

Příklad:
Regulárnímu výrazu i\{1,3\} vyhoví řetězce "i", "ii" nebo "iii".

Tvar tohoto opakovátka je velmi variabilní. Pokud chybí horní mez ( \{min,\}), znamená to, že maximální počet opakování je neomezený. Jestliže v konstrukci použijete jen samotné číslo ( \{počet\}), musí se regulární výraz opakovat přesně daný počet-krát.

Příklad:
Regulární výraz pro rodné číslo by vypadal takto:
[0-9]\{6\}/[0-9]\{3,4\}
Šest číslic, lomítko a ještě tři nebo čtyři číslice.

A již tu máme první odlišnost. V některých verzích regulárních výrazů (například v Perlu) se při omezování počtu opakování před složenými závorkami nepíší zpětná lomítka. Zapíšete-li zde \{ , znamená to, že zkoumaný text má obsahovat znak "{". Perl má pro tuto specialitu dobrou omluvu: zavedl pravidlo, že kombinace zpětného lomítka a znaku odlišného od písmena či číslice nikdy nemá speciální význam a vždy představuje dotyčný znak.

Nejpopulárnější opakovačky

Dva velmi populární případy opakování si vysloužily svůj vlastní speciální znak. Prvním je "alespoň jeden výskyt" - tedy cosi velmi podobného klasické opakovací hvězdičce, až na to, že opakovaný regulární výraz nelze vynechat. Stejného efektu dosáhnete konstrukcí \{1,\}, ale to je příliš složité psaní. Proto se alespoň jeden výskyt předchozího regulárního výrazu zapisuje znakem plus ( + ).

Druhou populární situací je nepovinný (čili nanejvýš jeden) výskyt. Opět jej lze zapsat pomocí \{0,1\} , ale kratší je otazník (? ).

Dialekty regulárních výrazů se u této dvojice znaků opět silně rozcházejí. Programy používající klasické regulární výrazy (grep, sed, vi) jim předřazují zpětné lomítko (\+ a \?). Generace, která implementuje rozšířené regulární výrazy, (egrep, awk, Perl) je píše bez něj ( + a ?).

Příklad:
Alespoň jedna číslice se tedy v grep, sed a vi vyjádří pomocí [0-9]\+, zatímco egrep, awk či Perl nabízí kratší [0-9]+. Zápis můžeme lehce rozšířit na regulární výraz pro celé číslo: nepovinné znaménko následované alespoň jednou číslici. V klasických regulárních výrazech to bude [-+]\?[0-9]\+ , zatímco v rozšířených [-+]?[0-9]+.

Pozice

Výrok "dejte mi pevný bod a pohnu zeměkoulí" jistě znáte. Nahlíženo jeho optikou byly všechny naše dosavadní regulární výrazy poněkud neukotvené. Řetězec, který jim vyhovuje, se mohl vyskytovat kdekoli ve zkoumaném textu. Občas však člověk musí být přísnější.

Proto regulární výrazy nabízejí několik speciálních pozičních znaků. Těmi nejznámějšími jsou stříška ( ^), která ztělesňuje začátek řádku (resp. zkoumaného řetězce znaků), a dolar ( $) označující jeho konec.

Příklad:
grep '^#' vám tedy najde řádky začínající znakem '#', grep '[0-9]$' řádky končící číslicí a konečně grep '^-\+$' řádky složené pouze z pomlček (nikoli však prázdné).

Dalším významným místem je hranice slova. Ve většině regulárních dialektů máte k dispozici konstrukci \< , která označuje začátek slova, a \>, které vyhoví pouze jeho konec.

Příklad:
Zajímají-li vás všechny řádky, na nichž se písmeno 'a' vyskytuje v roli jednopísmenné spojky, nasaďte
grep '\<a\>'

Shrnutí

výrazznamená
*libovolný počet opakování předchůdce
+ nebo \+ alespoň jeden výskyt předchůdce
? nebo \? nanejvýš jeden výskyt předchůdce
{min,max} nebo \{min,max\} alespoň min a nanejvýš max výskytů předchůdce
^začátek řádku
$konec řádku
\< nebo \b začátek slova
\> nebo \b konec slova


V této části ukážeme snad nejsilnější prvek regulárních výrazů - jejich paměť. Regulární výraz si totiž dokáže zapamatovat řetězec, který vyhověl jeho části, a později jej použít. Největší služby tento mechanismus odvede při nahrazování.

Zapamatuj a vzpomeň si

Prostředky pro zapamatování jsou směšně jednoduché. Část, kterou si má regulární výraz podržet v paměti, prostě ohraničíte konstrukcemi \( a \). Takových úseků můžete v regulárním výrazu mít hned několik. Dokonce je lze i vzájemně vnořovat.

Když později chcete použít zapamatovaný řetězec, napište \číslo , kde číslo je pořadové číslo zapamatovaného úseku. Pořadová čísla začínají jedničkou a rozhoduje o nich pořadí levé (otevírací) závorky zapamatovávané sekvence.

Příklad:
Podrobíte-li řádek ze souboru /etc/passwd regulárnímu výrazu ^\([^:]*\):[^:]*:\([^:]*\) , bude \1 obsahovat přihlašovací jméno a \2 jemu odpovídající identifikátor (UID).

O dobrodiní paměti jste ochuzeni u programů egrep a awk (U awk vzorků, ve řetězcových funkcích gensub, gsub, sub je paměť podporována). Jejich algoritmus pro srovnávání regulárních výrazů nepodporuje zapamatování (a z principiálních důvodů ani podporovat nemůže).

Použití při hledání

Použití zapamatovaných částí při vyhledávání je poměrně vzácné. Řečeno mluvou politiků: společenská potřeba takové služby je celkem malá. Příklady sice existují, nicméně bývají takové školometské. Tak si nějaké zkusíme.

Příklad:
Hezkou a zcela neužitečnou ilustrací zapamatování je hledání palindromů (slov, která se nezmění, když je čtete pozadu). Tak například výrazu
\<\(.\)\(.\).\2\1\>
vyhoví právě a pouze pětiznakový palindrom (např. radar či rotor). První dva znaky slova si zapamatuje, za nimi následuje třetí libovolný znak, čtvrtý musí být stejný jako zapamatovaný druhý a pátý znak se musí shodovat s prvním.
Příklad:
Zkusím něco, co by alespoň vzdáleně připomínalo reálný život. Řekněme, že máte výstupy z jakéhosi algoritmu - na každém řádku sadu čísel oddělených mezerami. Každý řádek zároveň končí správným výsledkem. Pokud algoritmus pracuje správně, poslední dvě čísla na řádku jsou totožná. Hledáme tedy řádky, ve kterých se poslední číslo liší od předposledního. K řešení poslouží grep s negovanou podmínkou (volba -v):
grep -v ' \([^ ]\+\) \+\1$'
Vzor začíná mezerou před předposledním číslem. Za ní následuje neprázdná posloupnost nemezerových znaků ([^ ]\+ ), která se zapamatuje. Po ní následuje alespoň jedna mezera (  \+) a znovu stejná posloupnost, za kterou už je jen konec řádku.

Použití při nahrazování

Daleko častěji se zapamatované řetězce vyskytují v příkazech pro nahrazování. Díky nim si můžete ze vstupních dat vytáhnout informace, které vás zajímají, a poskládat si je do tvaru, který potřebujete.

Příklad:
Běžný problém všedního dne: potřebujete u skupiny souborů změnit příponu z .htm na .html. Pro podobné účely sice existují různá udělátka, ale je třeba si je doinstalovávat a práce s nimi nebývá úplně snadná. takže se podívejme, jak poslouží standardní nástroje, které najdete v každém Unixu.

Postup je jednoduchý: obstaráte si seznam jmen souborů, každé jméno pak změníte na příkaz mv staré nové a tyto příkazy provedete. Popsaný postup lze realizovat třeba takto:

ls *.htm > seznam
sed 's/\(.*\)/mv \1 \1l/' seznam > akce
chmod a+x akce
./akce
rm seznam akce
Uznávám, že prosté připojení "l" na konec jména souboru je dosti snadnou modifikací. Složitější věci však znamenají jen úpravu příkazu s . Například změnu přípony z .doc na .txt by zajistilo s/\(.*\)\.doc/mv \1.doc \1.txt/ - zapamatuje si jen vlastní jméno souboru a přípony jsou explicitně vyjmenovány.
Příklad:
A teď něco drsnějšího. Chtěl bych ze souboru /etc/passwd vyrobit seznam domácích stránek uživatelů. Takže potřebuji řádky transformovat z původní podoby
uživatel:heslo:UID:GID: vlastní jméno :...
na
<A HREF="/~ uživatel"> vlastní jméno</A>
Kýženým substitučním příkazem, který to zařídí, je
s/\([^:]*\):\([^:]*:\)\{3\}\([^:]*\).*/<A HREF="/~\1">\3<\/A>/
Jak vidíte, prostřednictvím závorek lze předepsat počet opakování i rozsáhlejšímu úseku regulárního výrazu - zde se třikrát opakuje skupina [^:]*:. V takovýchto situacích závorky většinou neslouží k zapamatování (i když si pochopitelně něco zapamatují), ale čistě k vymezení opakované části. Ta má - bez ohledu na skutečný počet opakování - jen jediné pořadové číslo. Proto se závěrečné zapamatované jméno uloží jako \3.

Shrnutí

výraz význam
\(výraz\) zapamatuje si text vyhovující výrazu
\1 první zapamatovaný řetězec

Od minula si umíte nalezené věci zapamatovat a později použít. To je v reálném životě šeredně nebezpečná vlastnost. Například jste teď nepoužitelní jako voliči. Abyste se opět stali politicky korektními, naučím vás pozměnit, co jste si zapamatovali.

Klasické a rozšířené regulární výrazy

Programy grep a egrep rozštěpily regulární svět na dva proudy. Každý z nich používal poněkud jinou variantu regulárních výrazů a také jiný algoritmus pro jejich hledání. Regulární výrazy grepu představují klasickou variantu, zatímco odrůdě reprezentované egrepem se říká rozšířené regulární výrazy.

Základní rozdíly rozšířených regulárních výrazů proti klasickým byly následující: zavedly speciální znaky + a ? pro alespoň jeden a nanejvýš jeden výskyt, znak | pro "nebo" a nepodporovaly mechanismus zapamatování.

Postupem času se však původní rozdíly smazávaly a obě odrůdy implementovaly konstrukce, které byly původně k dispozici jen u konkurence. Takže mezi současným GNU grepem a GNU egrepem je rozdíl pouze v tom, kde se píší zpětná lomítka.

V předminulém odstavci jste možná zastříhali ušima, protože jsem se zmínil o dosud utajené konstrukci. Jedná se o "nebo", které se zapisuje v podobě svislé čáry (|). U klasických regulárních výrazů se před ni zapisuje zpětné lomítko (pokud je vůbec k dispozici).

"Nebo" má nižší prioritu než zřetězení, takže například egrep 'raz|dva' najde řádky obsahující řetězec "raz" nebo "dva". Pokud potřebujete účinek omezit, použijte závorky.

Příklad:
Následující příkaz vyhledá řádky, které začínají řetězcem "From:" nebo "Subject:" (může se hodit například pro prohledávání vaší poštovní schránky):
egrep '^(From|Subject):'
V podání grepu by příkaz vypadal takto:
grep '^\(From\|Subject\):'




Poslední aktualizace: 17.3.2004 Fr. Hanzlík