GNU/Linux >> Znalost Linux >  >> Linux

Proč regulární výraz funguje v X, ale ne v Y?

Napsal jsem regulární výraz, který dobře funguje v určitém programu (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, …). Ale když jej použiji v jiném programu (nebo na jiné unixové variantě), přestane odpovídat. Proč?

Přijatá odpověď:

Bohužel z historických důvodů mají různé nástroje mírně odlišnou syntaxi regulárních výrazů a někdy mají některé implementace rozšíření, která nejsou podporována jinými nástroji. I když existuje společný základ, zdá se, že každý autor nástrojů udělal různá rozhodnutí.

Důsledkem je, že pokud máte regulární výraz, který funguje v jednom nástroji, možná jej budete muset upravit, aby fungoval v jiném nástroji. Hlavní rozdíly mezi běžnými nástroji jsou:

  • zda operátory +?|(){} vyžadovat zpětné lomítko;
  • jaká rozšíření jsou podporována nad rámec základních .[]*^$ a obvykle +?|()

V této odpovědi uvádím hlavní standardy. Podrobnosti najdete v dokumentaci k nástrojům, které používáte.

Porovnání motorů regulárních výrazů na Wikipedii obsahuje tabulku se seznamem funkcí podporovaných běžnými implementacemi.

Základní regulární výrazy (BRE)

Základní regulární výrazy jsou kodifikovány standardem POSIX. Je to syntaxe, kterou používá grep , sed a vi . Tato syntaxe poskytuje následující funkce:

  • ^ a $ shodu pouze na začátku a na konci řádku.
  • . odpovídá libovolnému znaku (nebo libovolnému znaku kromě nového řádku).
  • […] odpovídá libovolnému znaku uvedenému v závorkách (znaková sada). Pokud je první znak po úvodní závorce ^ , místo toho se shodují znaky, které nejsou uvedeny. Chcete-li zahrnout ] , vložte jej bezprostředně za úvodní [ (nebo za [^ pokud je to negativní sada). Pokud - je mezi dvěma znaky, označuje rozsah; zahrnout doslovný - , umístěte jej tam, kde jej nelze analyzovat jako rozsah.
  • Zpětné lomítko před kterýmkoli z ^$.*[ cituje další znak.
  • * odpovídá předchozímu znaku nebo podvýrazu 0, 1 nebo vícekrát.
  • (…) je syntaktická skupina pro použití s ​​* operátor nebo zpětné odkazy a DIGIT náhrady.
  • Zpětné odkazy 1 , 2 , … porovnejte přesný text odpovídající odpovídající skupině, např. (fo*)(ba*)1 odpovídá foobaafoo ale ne foobaafo . Neexistuje žádný standardní způsob, jak odkazovat na 10. skupinu a další (standardní význam 10 je první skupina, za kterou následuje ).

Následující funkce jsou také standardní, ale v některých omezených implementacích chybí:

  • {m,n} odpovídá předchozímu znaku nebo podvýrazu mezi m do n časy; n nebo m lze vynechat a {m} znamená přesně m .
  • Uvnitř hranatých závorek lze použít třídy znaků, například [[:alpha:]] odpovídá libovolnému písmenu. Moderní implementace výrazů hranatých závorek) také zahrnují porovnávací prvky jako [.ll.] a třídy ekvivalence jako [=a=] .

Následují běžná rozšíření (zejména v nástrojích GNU), ale nenacházejí se ve všech implementacích. Podívejte se do návodu k nástroji, který používáte.

  • | pro alternaci:foo|bar odpovídá foo nebo bar .
  • ? (zkratka pro {0,1} ) a + (zkratka pro {1,} ) shoduje se s předchozím znakem nebo podvýrazem nejvýše jednou nebo nejméně jednou.
  • n odpovídá novému řádku, t odpovídá kartě atd.
  • w odpovídá libovolnému slovnímu prvku (zkratka pro [_[:alnum:]] ale s obměnami, pokud jde o lokalizaci) a W odpovídá libovolnému znaku, který není součástí slova.
  • < a > porovnejte prázdný řetězec pouze na začátku nebo na konci slova; b odpovídá buď, a B odpovídá kde b ne.

Všimněte si, že nástroje bez | operátor nemají plnou sílu regulárních výrazů. Zpětné odkazy umožňují několik dalších věcí, které nelze provést s regulárními výrazy v matematickém smyslu.

Rozšířené regulární výrazy (ERE)

Rozšířené regulární výrazy jsou kodifikovány standardem POSIX. Jejich hlavní výhodou oproti BRE je pravidelnost:všechny standardní operátory jsou holé interpunkční znaménka, zpětné lomítko před interpunkčním znaménkem je vždy cituje. Je to syntaxe, kterou používá awk , grep -E nebo egrep , GNU sed -r a bashův =~ operátor. Tato syntaxe poskytuje následující funkce:

  • ^ a $ shodu pouze na začátku a na konci řádku.
  • . odpovídá libovolnému znaku (nebo libovolnému znaku kromě nového řádku).
  • […] odpovídá libovolnému znaku uvedenému v závorkách (znaková sada). Doplnění s počátečním ^ a rozsahy fungují jako v BRE (viz výše). Třídy znaků lze použít, ale v několika implementacích chybí. Moderní implementace také podporují třídy ekvivalence a prvky řazení. Zpětné lomítko v závorkách uvozuje další znak v některých, ale ne ve všech implementacích; použijte \ znamená zpětné lomítko pro přenositelnost.
  • (…) je syntaktická skupina pro použití s ​​* nebo DIGIT náhrady.
  • | pro alternaci:foo|bar odpovídá foo nebo bar .
  • * , + a ? odpovídá předchozímu znaku nebo podvýrazu několikrát:0 nebo více pro * , 1 nebo více pro + , 0 nebo 1 pro ? .
  • Zpětné lomítko uvozuje další znak, pokud není alfanumerický.
  • {m,n} odpovídá předchozímu znaku nebo podvýrazu mezi m a n časy (u některých implementací chybí); n nebo m lze vynechat a {m} znamená přesně m .
  • Některá běžná rozšíření jako v BRE:DIGIT zpětné odkazy (zejména chybí v awk kromě implementace busybox, kde můžete použít $0 ~ "(...)\1" ); speciální znaky n , t , atd.; hranice slov b a B , slovní prvky b a B , …
Související:Moje předchozí teorie byla špatná, tak proč to funguje?

PCRE (regulární výrazy kompatibilní s Perl)

PCRE jsou rozšíření ERE, původně představená Perlem a přijatá GNU grep -P a mnoho moderních nástrojů a programovacích jazyků , obvykle prostřednictvím knihovny PCRE. Pěkné formátování s příklady najdete v dokumentaci k Perlu. Ne všechny funkce nejnovější verze Perlu jsou podporovány PCRE (např. spouštění kódu v Perlu je podporováno pouze v Perlu). Přehled podporovaných funkcí naleznete v příručce PCRE. Hlavní doplňky ERE jsou:

  • (?:…) je nezachycující skupina:jako (…) , ale nepočítá se pro zpětné reference.
  • (?=FOO)BAR (lookahead) odpovídá BAR , ale pouze pokud existuje shoda pro FOO začínající na stejné pozici. To je nejužitečnější pro ukotvení shody bez zahrnutí následujícího textu do shody:foo(?=bar) odpovídá foo ale pouze pokud za ním následuje bar .
  • (?!FOO)BAR (negativní předhled) odpovídá BAR , ale není zde také shoda pro FOO na stejné pozici. Například (?!foo)[a-z]+ odpovídá jakémukoli malému slovu, které nezačíná foo; [a-z]+(?![0-9) odpovídá jakémukoli malému slovu, které není následováno číslicí (takže v foo123 , odpovídá fo ale ne foo ).
  • (?<=FOO)BAR (lookbehind) odpovídá BAR , ale pouze v případě, že mu bezprostředně předchází shoda pro FOO . FOO musí mít známou délku (nelze použít operátory opakování, jako je * ). To je nejužitečnější pro ukotvení shody bez zahrnutí předchozího textu do shody:(?<=^| )foo odpovídá foo ale pouze pokud předchází mezera nebo začátek řetězce.
  • (?<!FOO)BAR (negativní lookbehind) odpovídá BAR , ale pouze v případě, že mu bezprostředně nepředchází shoda pro FOO . FOO musí mít známou délku (nelze použít operátory opakování, jako je * ). To je nejužitečnější k ukotvení shody bez zahrnutí předchozího textu do shody:(?<![a-z])foo odpovídá foo ale pouze v případě, že mu nepředchází malé písmeno.

Emacs

Syntaxe Emacsu je mezi BRE a ERE. Kromě Emacsu je to výchozí syntaxe pro -regex v GNU najít. Emacs nabízí následující operátory:

  • ^ , $ , . , […] , * , + , ? jako v ERE
  • (…) , | , {…} , DIGIT jako v BRE
  • více sekvencí zpětného lomítka; < a > pro hranice slov; a další v posledních verzích Emacsu, které často nejsou podporovány v jiných motorech se syntaxí podobnou Emacsu.
Související:Použijte find k nalezení určitého adresáře a smazání všech souborů v něm kromě jednoho adresáře?

Skořápky

Shell globs (zástupné znaky) provádějí porovnávání vzorů se syntaxí, která je zcela odlišná od regulárních výrazů a je méně výkonná. Kromě shellů jsou tyto zástupné znaky dostupné s dalšími nástroji, jako je find -name a rsync filtry. Vzory POSIX zahrnují následující funkce:

  • ? odpovídá libovolnému jednotlivému znaku.
  • […] je znaková sada jako v běžných syntaxích regulárních výrazů. Některé shelly nepodporují třídy znaků. Některé shelly vyžadují ! místo ^ k negaci množiny.
  • * odpovídá libovolné sekvenci znaků (často kromě / při porovnávání cest k souboru; if / je vyloučeno z * a poté ** někdy obsahuje / , ale podívejte se do dokumentace nástroje).
  • Zpětné lomítko uvozuje další znak.

Ksh nabízí další funkce, které poskytují jeho vzoru odpovídající plnou sílu regulárních výrazů. Tyto funkce jsou také dostupné v bash po spuštění shopt -s extglob . Zsh má jinou syntaxi, ale může také podporovat syntaxi ksh po setopt ksh_glob .


Linux
  1. Proč substituce procesu Bash nefunguje s některými příkazy?

  2. Proč `md5sum` nedává stejný hash jako internet?

  3. Proč překladový soubor Bash neobsahuje všechny chybové texty?

  1. Linux – Proč Setuid nefunguje?

  2. Definice regulárního výrazu?

  3. Proč dokument Parent Shell Here nefunguje pro dílčí příkaz v Dash, ale funguje Bash?

  1. Proč „ukončit &“ nefunguje?

  2. Proč nefunguje find -exec mv {} ./target/ +?

  3. Proč Tomcat pracuje s portem 8080, ale ne s 80?