Регулярни изрази за търсене във файлове, egrep

 

1      Въведение

2.       Търсене във файлове с egrep. 1

3.       Съвпадения на шаблони (pattern matching). 1

Логическо „или“. 1

Групиране. 1

Квантификатори. 1

grep и egrep. 1

4.     Примери

5.       Допълнителна информация. 1

 

1.     Въведение

 Регулярният израз (regular expression или regex) представлява текстов шаблон (pattern) , който се използва от команди като grep, egrepsed (stream editor for filtering and transforming text), awk (scripting language used for manipulating data and generating reports), езици като Perl, Javascript  и много други приложения за откриване на "текстов низ" (string of text) във файл. Под "текстов низ" се разбира символ, дума, изречение или специален шаблон (pattern) от символи.  Използва се програма (regular expression engine) за транслиране на шаблона при сравняване с обработвания текст.

Unix/Linux използва два вида regular expression engine:

Повечето Unix/Linux програми работят с BRE, но има и много, които ползват ERE.

Тъй като има много regex реализации, в настоящата глава ще бъдат разгледани само на-често използваните и ще бъдат демонстрирани с командата egrep  (или grep -E), която е акроним на "Extended Global Regular Expressions Print".   

     2.     Търсене във файлове с egrep

Командата egrep извежда в stdout всички редове, които съответстват на даден шаблон (pattern), зададен като първи параметър на командата, намерени в  stdin (или от файловете зададени като останалите параметри на командата)

$ egrep [command line options] <pattern> [<filename>]

Някой често използвани опции:

·        -i               прави търсенето да не различава малките и главните букви.

·        -r               търси рекурсивно в файлове в специфицираните директории.

·        -l               извежда само имената на анализираните файлове , които съдържат търсения шаблон.

·        -c               извежда броя на съвпаденията.

·        -n              номерира анализираните редове.

·        -v              обръща теста – извежда редовете без съвпадения.

Например за извеждане на екрана (stdout) всички редове въведени от клавиатурата (stdin ), които съдържат низа aha командата се подава без опции с "aha" като шаблон и без втори параметър:

$ egrep "aha"

rty

ahasd

ahasd

rahat

rahat

^D

$

Ако бъде подадена опция  -c:

$ egrep -c "aha"

rty

ahasd

rahat

^D

2

$

Ако бъде подадена опция  -n

$ egrep -n "aha"

rty

ahasd
2:ahasd

rahat
3:rahat

^D

$

Нека файлът regex.txt да съдържа ( може да бъде създаден например с cat или с редактор nano) :

$ cat > regex.txt
egrep stands for:
extended
global
regular
expression
print
$

Файлът може да бъде претърсен за редове съдържащи някакъв символ или низ от символи. Например за извеждане на екрана на всички редове от файла съдържащи символа 't' :

$ egrep "t"  regex.txt
egrep stands for:
extended
print
$

За извеждане на редовете съдържащи (или не) низа 'ex' :

$egrep -n "ex" regex.txt
2:extended
5:expression

$ egrep -nv "ex" regex.txt
1:egrep stands for:
3:global
4:regular
6:print
$


        3.        Съвпадения на шаблони (pattern matching)

Командата egrep се използва както за намиране на прости низове, така и на шаблони.  

Шаблоните се задават с регулярни изрази (regular expressions, regex). Регулярните изрази се използват за описание на множество от низове и се състоят от обикновени символи и от мета-символи със специално значение (операции). Метасимволите са:

.*[]^${}\+?|()

. Matches any single character (except newlines, normally)
\ Escape a special character (e.g. \. matches a literal dot)
? The preceding character may or may not be present (e.g. /hell?o/ would match hello or helo)
* Any number of the preceding character is allowed (e.g. .* will match any single-line string, including an empty string, and gets used a lot)
+ One or more of the preceding character (.+ is the same as .* except that it won’t match an empty string)
| “or”, match the preceding section or the following section (e.g. hello|mad will match “hello” or “mad”)
() group a section together. This can be useful for conditionals ((a|b)), multipliers ((hello)+), or to create groups for substitutions 
{} Specify how many of the preceding character (e.g. a{12} matches 12 “a”s in a row)
[] Match any character in this set. - defines ranges (e.g. [a-z] is any lowercase letter), ^ means “not” (e.g. [^,]+ match any number of non-commas in a row)
^ Beginning of line
$ End of line

Така например, ако се  използва метасимволът ^ в началото на низа, то търсенето на символ или низ се ограничава само до началото на линията:

$ egrep  "^g" regex.txt
global
$

Ако мета-символ трябва да се търси като обикновен символ, той трябва да се предшества от "escape" - '\':

$ egrep "\^g"

gkpp
ssss^gjjj
ssss^gjjj

$

Метасимволът $ зададен в края на низа го задължава да се намира в края на линията:

$ egrep "t$"  regex.txt
print
$

Метасимволът точка . замества произволен символ. Така например 'a.c' описва множеството от низове "abc", "akc", "a-c", …  (но не и "ac")!

$ egrep "a.c"
abc
abc
akc
akc
ac
$

Логическо „или“ 

Вертикална черта | задава алтернативи. Така например 'gray|grey' и  описва множеството от низове  "gray" и "grey".

 

Групиране

Малки скоби се използват за описване област на действие и подреждане на низовите последователности.

Например 'gray|grey' и 'gr(a|e) y' са еквивалентни.

           Пример: Сравнете  "gr(a|e)y"  и  "gra|ey"

Средни скоби се използват за изграждане на символен клас ( един символ зададен с алтернативи от символи). В съвпадението участва всеки единичен символ от класа, а последователност от символи се задава с три символа: първи и последен символ свързани със средно тире. Обикновените символи и последователностите могат да се смесват.

Например 'gr[aep-s]y' и 'gr(a|e|p|q|r|s) y' са еквивалентни.

Тирето в началото или в края на средните скоби е обикновен символ:.  [-ab]  ,  [ab-]  

$ egrep "k[-ab]r" 
kar
kar
ker
kbr
kbr
k-r
k-r
kr
$

Символите точка (".") и вертикална черта ("|") в средни скоби са обикновени, а не метасимволи.

$ egrep "k[.|a-]"
k.
k.
k|
k|
ka
ka
k-
k-
ke
$

Символът ^  поставен в началото на средни скоби замества произволен символ, който не се съдържа в скобите.  Следващият пример извежда  от текста за лиценза GPL-3 всички линии съдържащи четирибукви, завършващи на  "ode" -  "mode", "modelisation", "lode", "Code" ..., но не и "code":

$ egrep  "[^c]ode" /usr/share/common-licenses/GPL-3

  1. Source Code.
    model, to give anyone who possesses the object code either (1) a
the only significant mode of use of the product.
notice like this when it starts in an interactive mode:

$ egrep  -n "[^c]ode" /usr/share/common-licenses/GPL-3
112:  1. Source Code.
261:    model, to give anyone who possesses the object code either (1) a
308:the only significant mode of use of the product.
653:notice like this when it starts in an interactive mode:
$

 

Квантификатори

Квантификатор зададен след символ или група специфицира колко пъти този елемент може да се появи. Най-честите квантификатори са въпросителна ?, звезда * и символът  + .

?                            0 или една поява на предходния елемент. Например  'colou?r'  дефинира множеството   "color" и "colour".

*                           0 или повече повторения на предходния елемент.  Например  'ab*c'  дефинира множеството "ac", "abc", "abbc", "abbbc" ...  

Ако трябва да се изведат всички линии от текста за лиценза GPL-3, които съдържат отваряща и затваряща скоба, а между тях - само латински букви и интервали:

$  egrep "\(([A-Za-z ]*)\)" /usr/share/common-licenses/GPL-3
 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
distribution (with or without modification), making available to the
than the work as a whole, that (a) is included in the normal form of
Component, and (b) serves only to enable use of the work with that
(if any) on which the executable work runs, or a compiler used to
  ...

+                            1 или повече повторения на предходния елемент.  Например  'ab+c' задава "abc", "abbc", "abbbc"…, но не и  "ac".

{n}                        Предходния елемент се среща точно  n пъти. Например '[0-9]{2}' означава комбинация от 2 цифри.

{min,}                  Предходния елемент се среща min или повече пъти.

{min,max}           Предходния елемент се среща най-малко min пъти, но не повече от max пъти..

 

grep и egrep

Командата egrep работи с разширени (extended) регулярни изрази и е еквивалентна на командата grep –E. При команда grep символите ?, +, {, |, (, и ) се третират като нормални символи. За да бъдат считани за метасимволи трябва да бъдат предшествани от обратна наклонена черта:    \?,  \+, \{, \|, \(, и \).

  4.     Примери 

Нека да бъде създаден текстов файл test_regex

$ cat > test_regex
Fred apples 20
Susy oranges 5
Mark watermellons 12
Robert pears 4
Terry oranges 9
Lisa peaches 7
Susy oranges 12
Mark grapes 39
Anne mangoes 7
Greg pineapples 3
Oliver rockmellons 2
Betty limes 14

Да се изведат линиите съдержащи mell:

$ egrep 'mell' test_regex
Mark watermellons 12
Oliver rockmellons 2
$

 За извеждане номерата на линиите:

$ egrep -n 'mell' test_regex
3:Mark watermellons 12
11:Oliver rockmellons 2
$

В колко линии се съдържа:

$ egrep -c 'mell' test_regex
2
$

 Редовете с поне 2 последователни гласни букви:

$ egrep '[aeiou]{2,}' test_regex
Robert pears 4
Lisa peaches 7
Anne mangoes 7
Greg pineapples 3

 Редовете с цифрата 2, която не е последна в линията:

$ egrep '2.+' test_regex
Fred apples 20
$

 С цифра 2 последна в линията :

$ egrep '2$' test_regex
Mark watermellons 12
Susy oranges 12
Oliver rockmellons 2
$

Линиите съдържащи is или go или or :

$ egrep 'or|is|go' test_regex
Susy oranges 5
Terry oranges 9
Lisa peaches 7
Susy oranges 12
Anne mangoes 7
$

 Линиите, които започват с буквите от А  до К:

$ egrep '^[A-K]' test_regex
Fred apples 20
Anne mangoes 7
Greg pineapples 3
Betty limes 14
$

         Съществува библиотека ( http://www.regexlib.com/ )  от създадени готови шаблони за по-сложни търсения.

          Нека опитаме нещо по-сложно - валидиране на  E-mail адреси.
         Създаване на  тестов файл - test1

$cat > test1
ivan.momtchev@tu-sofia.bg
ivan
ivo@tu-sofia.bg
ivo@tu-sofia.bg@tu-sofia.bg
username@hostname.com
$

$ cat test1
ivan.momtchev@tu-sofia.bg
ivan
ivo@tu-sofia.bg
ivo@tu-sofia.bg@tu-sofia.bg
username@hostname.com
$

             
           
Проверка дали даден низ е E-mail адрес (шаблонът е взет от http://www.regexlib.com/ ) и не съответства напълно на  стандарта  RFC 5322:

$
$ egrep '^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$' test1
ivan.momtchev@tu-sofia.bg
ivo@tu-sofia.bg
username@hostname.com 
$

            Допълнителна информация за шаблон за проверка на  E-mail адрес  по стандарта  RFC 5322 може да видите видите тук.  

        5.        Допълнителна информация

$man 7 regex
$man egrep