Linux: Kniha kouzel, vanilková příchuť 2.14 (15. července 2022)
Veškerá moc příkazové řádky/příkazového řádku přehledně, pro začátečníky i pokročilé

13. Regulární výrazy

Řada 2.x vanilkové příchuti Linuxu: Knihy kouzel je od 15. července 2022 do 1. března 2025 ve stavu dlouhodobé pasivní údržby; nahlášené chyby budou opravovány, ale aktivní vývoj se již věnuje jiným projektům. Máte-li zájem pokračovat v tvorbě Linuxu: Knihy kouzel pro novější verze linuxových operačních systémů, kontaktujte autora nebo rovnou vytvořte odnož.

1. Úvod

Regulární výrazy představují velmi silný nástroj k vyhledávání, analýze, filtrování, kontrole syntaxe, extrakci a transformaci textů a textových dat. Slouží k formálnímu popisu (obvykle nekonečné) množiny řetězců a následné vyhledávání řetězců z této množiny v textu. S takto vyhledanými řetězci (tzv. výskyty či shodami) můžeme dále pracovat.

Regulární výrazy se objevují se ve většině programovacích jazyků, ale také v textových editorech či manuálním nastavení e-mailových filtrů.

V linuxu se bohužel vyskytují tři různé syntaxe regulárních výrazů – základní regulární výrazy, rozšířené regulární výrazy a regulární výrazy jazyka Perl. Z praktických důvodů považuji za nejdůležitější rozšířené regulární výrazy, a proto kdykoliv napíšu „regulární výraz“ bez dalšího upřesnění, mám na mysli rozšířený regulární výraz. Kde se od sebe syntaxe liší, bude to u zaklínadel upřesněno; kde není uvedena samostatná varianta pro Perl, platí pro Perl varianta pro rozšířený regulární výraz.

GNU Bash je vyvíjen v rámci projektu GNU.

2. Definice

  • Řetězec se shoduje s daným regulárním výrazem, pokud patří do množiny řetězců, kterou regulární výraz definuje, tedy pokud odpovídá požadavkům regulárního výrazu jako celek, od začátku do konce. Např. s regulárním výrazem „a.c“ se shoduje řetězec „abc“, protože „a“ v regulárním výrazu přijímá „a“ z řetězce; tečka z regulárního výrazu přijímá jakýkoliv znak, tedy i „b“ v řetězci a „c“ v regulárním výrazu přijme „c“ z řetězce. S tímtéž regulárním výrazem se už ale neshodují řetězce „Abc“ (pokud nevypnete rozlišování velkých a malých písmen), „abb“, „abbc“ či „abcc“ (protože k poslednímu „c“ už v regulárním výrazu není nic, co by ho přijalo).
  • Shoda (match) je nejlevější a nejdelší (u tzv. „nehladového prohledávání“ naopak nejkratší) podřetězec prohledávaného řetězce, který se shoduje s daným regulárním výrazem, a totéž rekurzivně pro zbytek řetězce za koncem shody. Takže shody jsou vlastně podřetězce shodující se s regulárním výrazem, ale jen tak, aby se nepřekrývaly. Shodou může být i celý prohledávaný řetězec (protože každý řetězec je sám svým podřetězcem).
  • Řetězec odpovídá danému regulárnímu výrazu, pokud s ním má nějakou shodu. Takže regulárnímu výrazu „a.c“ odpovídají např. řetězce „abc“, „aabc“, „xaxcx“, „xaxcxaxcx“ apod., ale ne „xaxbxc“.
  • Jako atom označuji nejkratší část (podřetězec) regulárního výrazu, která končí na dané pozici a tvořila by syntakticky správný regulární výraz sama o sobě. Atomem je např. „a“, „[abc]“, „(a|b)?“ či „\s+“, ale ne „a|b“, protože „b“ je kratší a samo o sobě tvoří syntakticky správný regulární výraz.
  • Kvantifikátor je speciální podřetězec, který se zapisuje za atom a určuje dovolený počet opakování.
  • Kotvahranice jsou speciální atomy k testování pozice, např. „^“ nebo „\<“. Z prohledávaného řetězce přijímají fiktivní prázdný podřetězec na jednoznačné pozici (u kotvy) nebo na všech pozicích splňujících určité podmínky (u hranice). Zvláštním případem hranice je vyhlížení.
  • Vzorek bashe (také vzorek) je regulární výraz v interpretu bash zapsaný jeho zvláštní syntaxí; slouží nejčastěji k filtrování názvů souborů. Při testování vzorků se obvykle vyžaduje úplná shoda.

3. Zaklínadla: Regulární výrazy

3/1 Jednotlivé znaky

@konkrétní znak#1 (1)
znak
@libovolný znak (rozšířený i základní/Perl)#2
.
(?:.|\n)
@kterýkoliv z uvedených znaků#3 (2)
[znaky]
@libovolný znak kromě uvedených#4
[^znaky]
@všechny znaky po první výskyt některého z uvedených/případně až do konce řetězce#5
[^znaky]*[znaky]
[^znaky]*
@bílý znak/nebílý znak#6
\s
\S
@desítková číslice (rozšířený/základní/Perl)#7
[0-9]
[0-9]
\d
@jiný znak než desítková číslice (rozšířený/základní/Perl)#8
[^0-9]
[^0-9]
\D
@závorky „()[]{}“ (rozšířený/ základní)#9
[(][)]\[\]\{\}
[(][)]\[\]{}
@libovolný alfanumerický znak, i národní abecedy (rozšířený/základní/Perl)#10
[[:alnum:]]
[[:alnum:]]
\w
@libovolný znak kromě alfanumerických (rozšířený/základní/Perl)#11
[^[:alnum:]]
[^[:alnum:]]
\W
@libovolné písmeno, i národní abecedy#12
[[:alpha:]]
@libovolné malé/velké písmeno, i národní abecedy#13
[[:lower:]]
[[:upper:]]

3/2 Kvantifikátory (operátory opakování)

@jednou nebo vůbec (≤ 1)(rozšířený/základní)#1
atom?
atom\?
@libovolněkrát (≥ 0)(rozšířený/základní)#2
atom*
atom*
@jednou nebo víckrát (≥ 1)(rozšířený/základní)#3
atom+
atom\+
@přesně N-krát (= N)(rozšířený/základní)#4
atom{N}
atom\{N\}
@M- až N-krát včetně (M ≤ počet ≤ N)(rozšířený/základní)#5
atom{M,N}
atom\{M,N\}
@minimálně M-krát (≥ M)(rozšířený/základní)#6
atom{M,}
atom\{M,\}
@maximálně N-krát (≤ N)(rozšířený/základní)#7
atom{,N}
atom\{,N\}
@snažit se opakovat co nejméně (nehladové prohledávání)(jen Perl)#8
atomkvantifikátor?

3/3 Operátor „nebo“

@některý z podvýrazů (rozšířený/základní)#1
výraz 1[|další výraz]
výraz 1[\|další výraz]

3/4 Kotvy a hranice (pozice)

@začátek testovaného řetězce (rozšířený i základní/víceřádkový režim/Perl)#1
^
\`
\A
@konec testovaného řetězce (rozšířený i základní/víceřádkový režim/Perl)#2
$
\'
\z
@začátek slova (rozšířený a základní/Perl)#3
\<
\b(?=\w)
@konec slova (rozšířený a základní/Perl)#4
\>
\b(?<=\w)
@začátek nebo konec slova (rozšířený/základní/Perl)#5
(\<|\>)
\(\<\|\>\)
\b
@začátek/konec řádku#6

3/5 Seskupení

@seskupení (rozšířený/základní)#1
(podvýraz)
\(podvýraz\)
@seskupení bez zapamatování (jen Perl)#2
(?:podvýraz)
@pojmenované seskupení (jen Perl)#3
(?<název>podvýraz)

3/6 Paměť (omezená podpora)

@původní podřetězec odpovídající celému regulárnímu výrazu (rozšířený a základní/Perl)#1 (3)
&
$&
@záchyt – podřetězec původního řetězce odpovídající seskupení (varianty)#2 (4)
\pořadové-číslo-1-až-9⊨ v reg. výrazu: GNU sed a Perl; v řetězci náhrady: GNU sed
\\pořadové-číslo-1-až-9⊨ v řetězci náhrady: GNU awk (jen funkce gensub())
$pořadové-číslo-1-až-9⊨ v řetězci náhrady: Perl
@totéž, ale první písmeno malé/velké#3 (5)
\l\pořadové-číslo-1-až-9\E
\u\pořadové-číslo-1-až-9\E
@totéž, ale celý text malými/velkými písmeny#4 (6)
\L\pořadové-číslo-1-až-9\E
\U\pořadové-číslo-1-až-9\E
@záchyt v Perlu v rámci regulárního výrazu (číslovaný/pojmenovaný)#5
\g{pořadové-číslo}
\g{název}

3/7 Vyhlížení (jen Perl)

@ověřit, že za aktuální pozicí následuje shoda s daným regulárním podvýrazem#1
(?=podvýraz)
@ověřit, že za aktuální pozicí nenásleduje shoda s daným regulárním podvýrazem#2
(?!podvýraz)
@ověřit, že aktuální pozici předchází shoda s daným regulárním podvýrazem#3
(?<=podvýraz)
@ověřit, že aktuální pozici nepředchází shoda s daným regulárním podvýrazem#4
(?<!podvýraz)

4. Zaklínadla: Vzorky bashe

4/1 Základní

@konkrétní znak#1 (7)
znak
@žádný či více libovolných znaků/jeden libovolný znak#2 (8)
*
?
@kterýkoliv z uvedených znaků#3 (9)
[znaky]
@kterýkoliv kromě uvedených znaků#4 (10)
[^znaky]
@bílý znak/nebílý znak#5
[[:space:]]
[^[:space:]]
@desítková číslice#6
[0-9]
@jiný znak než desítková číslice#7
[^0-9]
@závorky „()[]{}“#8
\(\)\[\]\{\}
@libovolný alfanumerický znak, i národní abecedy#9
[[:alnum:]]
@libovolný znak kromě alfanumerických#10
[^[:alnum:]]
@libovolné písmeno, i národní abecedy#11
[[:alpha:]]
@libovolné malé/velké písmeno, i národní abecedy#12
[[:lower:]]
[[:upper:]]

4/2 Rozšířené (s kvantifikací)

Podvzorek v následujících zaklínadlech je jakýkoliv vzorek, který by byl platný sám o sobě.

@některý z podvzorků#1
// Vnořený operátor @() lze skombinovat s obklopujícím operátorem, takže např. místo „+(@(A|B))“ stačí napsat „+(A|B)“, což má tentýž význam.
@(podvzorek[|další-podvzorek])
@podvzorek jednou nebo vůbec (≤ 1)#2
?(podvzorek)
@podvzorek libovolněkrát (≥ 0)#3
*(podvzorek)
@podvzorek jednou nebo víckrát (≥ 1)#4
+(podvzorek)
@žádný z podvzorků (negovaný test)#5
!(podvzorek[|další-podvzorek])
@všechny znaky po první výskyt některého z uvedených/případně až do konce řetězce#6
*([^znaky])[znaky]
*([^znaky])
@přesně N-krát (= N)#7
@M- až N-krát včetně (M ≤ počet ≤ N)#8
@minimálně M-krát (≥ M)#9
@maximálně N-krát (≤ N)#10

5. Parametry příkazů

5/1 bash

[[ řetězec =~ regulární-výraz ]]

Poznámka: Protože pravidla odzvláštňování regulárního výrazu v této konstrukci jsou neintuitivní a nepraktická, striktně doporučuji zadávat regulární výraz jako proměnnou, např. takto:

if [[ "$1/$2" =~ $regex ]]

Pozor! Proměnnou s regulárním výrazem v této konstrukci nikdy neuzavírejte do uvozovek! Pokud to uděláte, bash ji bude interpretovat jako obyčejný řetězec, ne jako regulární výraz!

Existuje ještě druhý přiměřeně funkční způsob:

[[ řetězec =~ $(printf %s "regulární výraz") ]]

Ale v tomto případě nesmí regulární výraz končit znakem nového řádku \n, protože ten by se při rozvoji ztratil; pokud tímto znakem končit musí, pomůže uzavřít ho do hranatých závorek („[\n]“).

[[ řetězec = vzorek ]]

Příklad:

[[ $prom = A@(0|1|2)C ]]

5/2 egrep

egrep parametry [-e] 'regulární výraz' [soubor]
-vLogická negace; hledat řádky, které nevyhovují výrazu.
-xRegulárnímu výrazu musí odpovídat celá řádka (výchozí chování: jakýkoliv podřetězec řádku).
-zŘádky vstupních souborů jsou ukončeny nulovým bajtem; znak \n bude považovat za za normální znak.
-C počet„kontext“ Kromě vyhovujícího řádku vypíše zadaný počet předchozích a následujících. (Samostatně lze tyto počty nastavit parametry -A-B.)
-oMísto celých řádek vypisuje jednotlivé podřetězce vyhovující výrazu, každý podřetězec na samostatnou řádku.
-hVyhledává-li se ve více souborech, neuvede se jako prefix řádky název souboru.
-HVždy uvede jako prefix řádku název souboru.
-nJako prefix bude vypisovat číslo řádky.
-qŽádný normální výstup, jen otestuje, zda by našel alespoň jeden vyhovující řádek. Parametr -s zase potlačí chybová hlášení.
-m NUkončí hledání po nalezení N vyhovujících řádek.
-iNerozlišovat velká a malá písmena.

Poznámka: příkaz „grep“ má tytéž parametry jako „egrep“, ale pracuje se základními regulárními výrazy.

5/3 gawk

gawk [parametry] '/regulární výraz/[{příkazy}]' soubor
-F hodnotaNastaví systémovou proměnnou „FS“ (field separator) na uvedenou hodnotu.
-v proměnná=hodnotaNastaví proměnnou na uvedenou hodnotu.
-SSpustí skript v „bezpečném režimu“, kdy nemůže volat příkazy bashe, spouštět jiné programy ani otevírat další soubory.

5/4 perl

perl -nwe 'if ($_ =~ /regulární výraz Perlu/) {print $_}' <vstupní-soubor
perl -pwe 's/regulární výraz Perlu/výraz náhrady/[g]' <vstupní-soubor
-nVykoná program v cyklu pro každou řádku vstupu.
-pVykoná program v cyklu pro každý řádek vstupu a na konci každého cyklu vypíše proměnnou „$_“ ($ARG).
-wZapne užitečná varování. (-W zapne všechna varování.)
-e programVykoná tento program místo načtení programu ze souboru.
-XVypne všechna varování.

5/5 sed

sed parametry [[-e příkazy] -e] příkazy [vstupní-soubor]
-EPoužívá rozšířené regulární výrazy místo základních.
-i„in-place“ Výstupem přepíše původní soubor.
-zŘádek končí nulovým bajtem, ne znakem \n.
-u„unbuffered“ Čte a zapisuje data po jednotlivých řádcích. (Normálně je kvůli výkonu načítá po delších blocích.)

6. Instalace na Ubuntu

Příkazy „egrep“, „grep“, „perl“ a „sed“ jsou základními součástmi Ubuntu. Příkaz „gawk“ je nutné doinstalovat, nebo místo něj použít méně schopný příkaz „awk“, který je základní součástí Ubuntu.

sudo apt-get install gawk

Regulární výrazy jsou používány i v mnoha dalších programech.

Rozšířené vzorky bashe jsou ve výchozím nastavení zapnuty pouze v interaktivním režimu. Ve skriptech a jiných případech, kdy jsou vypnuty, je potřeba je zapnout příkazem:

shopt -s extglob

7. Tipy a zkušenosti

  • V Perlu se k označení desítkové číslice běžně používá podvýraz „\d“; v jiných syntaxích regulárních výrazů ovšem není podporován, proto doporučuji zvyknout si na podvýraz „[0-9]“, který je čitelnější a je podporovaný opravdu všude.
  • Parametr -o u příkazu „egrep“ lze efektivně využít při počítání ne-ASCII znaků. Počet znaků č, š a ž v textu zjistíte příkazem „egrep -o '[čšž]' | wc -l“.
  • Regulární výrazy jsou přezdívány „write-only language“, protože bývá výrazně snazší je napsat než přečíst a pochopit. Než začnete rozumět cizím regulárním výrazům, musíte získat značné zkušenosti s psaním svých vlastních.
  • Ve výrazech typu „"[^"]*"“ často zapomínám kvantifikátor + či *.
  • Příručka varuje, že testování komplikovaných rozšířených vzorků může být pomalé. Pokud na to narazíte, může váš skript urychlit, když je přepíšete na regulární výrazy a k jejich testování použijete externí příkaz jako např. „egrep“.
  • Vzorky začínající „?“, „*“ nebo „[znaky]“ ignorují skryté soubory! Pravidlo říká, že obecné znaky jako „?“, „*“ a „[]“ nepokrývají tečku, která je prvním znakem názvu souboru. Toto chování lze vypnout nastavením „shopt -s dotglob“. Všechny názvy souborů a adresářů včetně „.“ a „..“ pokrývá rozšířený vzorek „?(.)*“.

8. Další zdroje informací

Pro samotné regulární výrazy je k dispozici velké množství webových stránek (viz Odkazy), ale většinou jsou zbytečné, protože s referenční příručkou (resp. s touto kapitolou) budete pravděpodobně schopen/a požadovaný regulární výraz vymyslet sám/a.

Pro zmíněné programy:

egrep --help
man gawk
perl --help

9. Zákulisí kapitoly

V této verzi kapitoly chybí:

  • nic

Tato kapitola záměrně nepokrývá:

  • nic
1 Některé znaky musejí být v regulárních výrazech pro zbavení svého speciálního významu odzvláštněny zpětným lomítkem.
2 Uvnitř těchto hranatých závorek se speciální znaky neodzvláštňují zpětným lomítkem, ale uvedením na určitou pozici.
3 Platí jen v rámci řetězce náhrady, ne v samotném regulárním výrazu.
4 Zdvojení zpětného lomítka v GNU awk vyplývá ze skutečnosti, že ho (obvykle) zadáváte jako řetězec v programovacím jazyce. Pokud byste náhodou např. načítali řetězec náhrady ze souboru, bude tam zpětné lomítko patřit pouze jedno!
5 Tuto variantu podporuje pravděpodobně jen Perl a sed a smí se vyskytnout pouze v řetězci pro náhradu, nikoliv přímo ve vlastním regulárním výrazu.
6 Viz poznámku k předchozímu zaklínadlu.
7 Znaky, které mají pro bash zvláštní význam, je nutno ve vzorku odzvláštnit, nejčastěji zpětným lomítkem.
8 V bashi tyto znaky nepokrývají tečku jako první znak skrytých souborů a adresářů.
9 Znaky „!“, „^“ a „-“ mají uvnitř těchto závorek zvláštní význam a při použití jako znaky je nutno je odzvláštnit! V bashi tato konstrukce nepokrývá tečku jako první znak skrytých souborů a adresářů.
10 Znaky „!“, „^“ a „-“ mají uvnitř těchto závorek zvláštní význam a při použití jako znaky je nutno je odzvláštnit! V bashi tato konstrukce nepokrývá tečku jako první znak skrytých souborů a adresářů.
Líbí se vám tento projekt a chcete, aby byl ještě lepší? Můžete mi s tím pomoci. Zmiňte se o něm technicky zdatným přátelům, opravte překlepy a nahlašte nefunkční zaklínadla, aby mohla být opravena; poskytněte mi zpětnou vazbu nebo se zapojte do vývoje nových kapitol. Další informace na GitHubu v dokumentu Jak se zapojit.
[BY-SA]

Veškerý obsah této stránky (text, obrázky, zdrojový kód) je možno upravovat a šířit pod podmínkami licence Creative Commons Attribution-ShareAlike 4.0 International. Upozorňuji, že uvedená licence vyžaduje uvedení seznamu autorů, licence a zdroje a poskytnutí stejné či kompatibilní licence k provedeným změnám, jsou-li nějaké. Příslušné údaje jsou dostupné na stránce „Přehled autorů“. Šíření obsahu bez těchto údajů nebo šíření upravené verze bez poskytnutí adekvátní licence k provedeným úpravám je pravděpodobně porušení licenčních podmínek a může být postihováno. Poskytování zdrojového kódu při šíření není touto licencí vyžadováno.

Pro nové verze, další informace, aktuální zdrojový kód a možnost se zapojit do projektu „Linux: Kniha kouzel“ navštivte jeho repozitář na GitHubu.