Linux: Kniha kouzel, 2.15 (1. března 2025)
Veškerá moc příkazové řádky/příkazového řádku přehledně, pro začátečníky i pokročilé

20. Regulární výrazy

Vývoj vanilkové příchuti Linuxu: Knihy kouzel byl 1. března 2025 ukončen. Tento text je zachován jako historický, ale chyby již nejsou opravovány. Odnože projektu pod kompatibilní licencí jsou vítány.

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ářů.
[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.