http://forum.ubuntu-fi.org/index.php?topic=44605.msg310928#msg310928
http://www.rexegg.com/regex-quantifiers.html

Kun halutaan saada tekstistä esiin tietyntyyppiset asiat niin tulostetaan teksti niiden tyyppisten asioiden regex:ään jolloin saadaan tuloste niistä kohdista jotka ovat muodoltaan tuota regex:ää vastaavia. Esimerkiksi kun halutaan saada tietää dokumentin päiväys niin tulostetaan dokumentti päivämäärä-regexään ja se tulostaa dokumentin päiväyksen olipa se dokumentin alussa, lopussa tai siinä välissä jossakin.

Regex kuvaa asioita seuraavasti: Jokaisella asialla on monia ominaisuuksia ja mikäli kahden asian kaikki ominaisuudet ovat samoja niin asiat ovat varmasti samoja. Koska regex ei yleensä voi kuvata asian kaikkia ominaisuuksia niin regex voikin sanoa vain että asiat ovat todennäköisesti samoja tai varmasti erilaisia - tosin todennäköisyys voi olla käytännön kannalta varmakin. Esimerkiksi seuraavat määritykset ovat voimassa:

                           pienten aakkosten regex on: [a-z] 
                            isojen aakkosten regex on: [A-Z]  
                           Merkit voidaan luetellakin: [asdfg]  
tai mikähyvänsä pienaakkonen paitsi joku luetelluista: [^asdfg] 
                                  numeroiden regex on: [0-9]
- regex toimii [kovissa]lainausmerkeissä: echo -5000 | grep '[-+0-9]*' Ilman lainausmerkkejä se on taiteilua: echo -5000 | tr -d +-[0-9]
                             

useat käytännön regex:t ovat hirviöitä ja tarkan muotin tekeminen on vaikeaa tai mahdotonta vähänkin monimutkaisissa tapauksissa ja sääntöjä on niin vaikeaa noudattaa, että regex:än muodostaminen on kokeellista hommaa ja teoriat vain kertovat mitä kannattaa kokeilla ja minkä pitäisi toimia. Sitäpaitsi en ole vielä nähnyt ohjetta joka ei olisi täynnä kirjoitusvirheitä ja jopa loogisia virheitä - niitä on ilmanmuuta tässäkin.

 Esimerkiksi sähköpostiosoite:
"^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"

ipv4:
\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

ipv6:
luku=-1e3; echo $luku | grep -Po  '[-+]?[0-9]*\.?[0-9]+[eE][-+]?\d+'echo 1:1:1:1:1:1:1:1111 | grep -P '^\s*(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$)){8,})((?1)(?>:(?1)){0,6})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}:|(?!(?:.*[a-f0-9]:){6,})(?3)?::(?>((?1)(?>:(?1)){0,4}):)?)?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?4)){3}))\s*$'

- päiväys:
echo 31.12.2001 | grep -P '(?>[0-2][0-9]|30|31)[.-](?>[0][0-9]|10|12)[.-][0-9][0-9][0-9][0-9]'  
vanha:  echo 31.12.2001 | egrep  ^[0-2''][0-9]\.[0-9][012'']\.[0-9][0-9][0-9][0-9]$\|^3[01]\.[0-9][012'']\.[0-9][0-9][0-9][0-9]$

 


**
Regex:iä voi yhdistellä, mutta sillä on väliä kuinka yhdistelyn tekee:
echo a1 | grep -P '[a-z][0-9]'  # tulostaa: a1
echo 1a | grep -P '[a-z][0-9]'  # ei tulosta mitään koska järjestys on väärä
echo 1a | grep -P '[a-z0-9]'    # tulostaa: 1a
echo a1 | grep -P '[a-z0-9]'    # tulostaa: a1
**
pienaakkosista tehdyn sanan sanan muotti on: [a-z]*  Siis muotti [a-z] nolla tai äärettömiä kertoja - siis myös tyhjä on mahdollinen. 
                     Melkein sama muotti on: [a-z]+  joka ainoana erona on se että tyhjä ei ole mahdollinen.
- mutta: echo juu | grep '[a-z]'  tulostaa täsmälleen saman kuin: echo juu | grep '[a-z]*' . On pakko selittää tässä kuinka sovellukset ottavat tästä ilon irti koska sitä ei missään muualla selitetä:
Kun esimerkiksi grep:lle annetaan käsiteltäväksi teksti alkaa se lähettää tekstistä merkkejä regex:älle alkaen merkistä 1 jatkaen siitä merkki kerrallaan kohti loppua. Kohdatessaan merkin joka sopii regex:än aletaan pitää kirjaa missä regex:ssä mennään ja toimia sen perusteella - tähän se backtracking taitaa viitata. Viisainta tyytyä toimimaan esimerkkien perusteella. 
 
Siten esimerkiksi:
1. echo juu | grep '[a-z]'    # tulostaa: juu 
2. echo juu | grep -o '[a-z]' # tulostaa:  
   j
   u
   u
3. echo juu | grep -o '[a-z]*'       # tulostaa: juu
4. echo "juu jaa" | grep -o '[a-z]*' # tulostaa:
   juu
   jaa
5. echo fdfdfalku..tarinaa..loppufdfvdvalku.*muutatarinaa..loppu | grep -o alku.*loppu # tulostaa: alku..tarinaa..loppu
6. echo "fdfdf<<..tarinaa..>>fdfvd<<..muuta tarinaa..>>" | grep -Po \<\<.*?\>\>        # tulostaa:
<<..tarinaa..>>
<<..muuta tarinaa..>>
** 
[a-z]?  on tyhjä tai yksi pienaakkonen
[a-z]\{5\}    jos sanassa on viisi pienaakkosta. 
.....         jos sanassa voi olla viisi mitähyvänsä merkiä 
a...b         jos sanassa on viisi merkkiä ja se alkaa a:lla ja loppuu b:hen
\<a...b\>     jos sanassa on viisi merkkiä ja se alkaa a:lla ja loppuu b:hen ja sana rajoittuu molemmilta puolilta välilyöntiin tai marginaaliin.
                  kun tulostaa sanakirjan tähän muottiin niin saa nopeasti listan sanaristikon täyttämiseen.
**
Regex vaatii aina sovelluksen joka osaa sitä käyttää. Tässä käytetään grep:iä esimerkkinä, mutta kyllä myös awk, sed ja BASH itsekin osaavat käyttää regex:ää kukin omalla tavallaan.
**
echo aaaa | grep -Po a+?      # tulostaa viidelle riville a
echo aaaa | grep -Po 'a{2,}?' # tulostaa kahdelle riville aa
**
echo ' 1911 aaaa' | grep -Ewo '19[0-9]{2}|20[0-9]{2}' # tuosta tekstissä olevat vuosiluvut
**
Regex:iin tutustumista vaikeuttaa se että kaikki asiat voidaan esittää miljoonalla tavalla, ja jokainen esittää regex:ät omien mieltymystensä mukaan. Regex:iin kannattaakin alkuunsa tutustua vain yhden henkilön kertomuksista. Esimerkiksi seuraavat yhtä merkkiä kuvaavat regex:ät ovat lähes samoja:
[a-zA-Z0-9!"#$%&'()*+,./:;<=>?@\^_`{|}~-] 
[[:print:]] 
.           (siis pelkkä piste. Yleensä käytetään pistettä mutta kyllä noille toisillekin käyttöä löytyy. Ja muitakin tapoja on siis vielä miljoona)
**
Regex:iin tutustumista vaikeuttaa myös se että "erityyppisiä" regex:iä on monenlaisia vaikka kaikista puhutaan samalla nimellä. Ehkäpä turvallisimpia tapoja käyttää regex:iä ovat esimerkiksi:
echo lause | grep -P 'regex'                 tai: grep -P 'regex' tiedosto 
echo lause | sed -r 's/regex/onnistui/g'     tai: cat tiedosto | sed -r 's/regex/onnistui/g'  
- mutta seuraavannäköisiä ovat mahdollisia ja esimerkiksi ne toimivat joskus paremmin toisissa Linux-versioissa:
echo lause | grep -E 'regex'                 tai: grep -E 'regex' tiedosto 
echo lause | grep 'regex'                    tai: grep 'regex' tiedosto  
- mutta jos käytät väärää et ehkä saa aikaiseksi mitään toimintaa.
**  
ankkurit:
- ne kiinnittävät regex:n toimintaa jollaintavoin
^         ankkuroi haun rivin alkuun; (siis regex on aina voimassa vain ensimmäisellä merkillä ja jatkossa vain niin kauan kun regex jokakerran löytyy. Välilyönti lasketaan normaaliksi
          merkiksi. Merkin ^ tulee olla hakutermissä ensimmäisenä sillä jos se on muualla niin se lasketaan tavalliseksi merkiksi. Mikäli ensimmäine merlli on tosiaan ^ niin sen
          eteen kirjoitetaan keno elikä merkki \ )    
$         ankkuroi haun rivin loppuun. $ pitää olla hakutermissä viimeisenä
\<        ankkuroi haun sanojen alkuun
\>        ankkuroi haun sanojen loppuun 
\b        ankkuroi muotin sananojen alkuun, loppuun tai rivin alkuun tai rivin loppuun.
\B        ankkuroi muotin sananojen alkuun tai loppuun -> siis sana ennen on välilyönti ja niin perässäkin 
\w        etsii konaisia sanoja
\W        kokonaiset sanat eivät kelpaa 
\s        "whitespace" elikkä välilyönti tai TAB
\S        mikä tahasa muu kuin whitespace
**
Merkkijoukot:
[[:alnum:]] -> [A-Za-z0-9]
[[:alpha:]] -> [A-Za-z] 	
[[:blank:]] -> [ \t] välilyönti tai TAB
[[:cntrl:]] -> [\x00-\x1F\x7F] kontrollimerkit
[[:digit:]] -> [0-9] , [^0-9] -> kaikki muut kuin numerot
[[:graph:]] -> [\x21-\x7E] näkyvät merkit
[[:lower:]] -> [a-z] pienet kirjaimet 
[^[:lower]] -> merkkijoukko jossa ei ole missään yhtääkään pientä kirjainta                            
[[:print:]] -> [\x20-\x7E] näkyvät merkit ja välilyönti
[[:punct:]] -> [][!"#$%&'()*+,./:;<=>?@\^_`{|}~-] 
[[:space:]] -> [ \t\r\n\v\f]   (=Whitespace characters)
[[:upper:]] -> [A-Z] suuret kirjaimet
[[:xdigit:]] -> [A-Fa-f0-9] hexadesimaalit
**
Muotissa voidaan viitata sellaisiin merkkeihin joilla on erikoismerkitys. Niiden edessä on aina merkki: \. Seuraavat escape-koodatut tunnetaan:
\t 	tab
\n 	rivinvaihto
\r 	enter
\f 	sivunvaihto
\a 	hälytyskellon kilauttaminen
\e 	escape
\033 	octaali-merkki
\x1B 	hexa-merkki
\c[ 	kontrollimerkki
\l 	pienikirjain
\u 	isokirjain
\L 	pienikirjain
\U 	isokirjain
\E 	tiedonsiirron loppumerkki, EOF
\Q 	lainausmerkki
\w 	Match a "word" character
\W 	Match a non-word character
\s 	whitespace 
\S 	ei-whitespace 
\d 	Match a digit character
\D 	Match a non-digit character
\b 	word boundary
\B 	non-word boundary
\A 	tekstijonon alku
\Z 	Match only at EOS, or before newline
\z 	Anchor 	Match only at end of string
\G 	Anchor 	Match only where previous m//g left off      
**
Esimerkiksi päivämäärät valetaan muotissa jonka regex on: [0123][0-9].[01][0-9].[20][0-9][0-9][0-9]  (siis: pp.kk.vvvv). Kun halutaan tulostaa pitkästä dokumentista siinä olevat päiväykset niin käsketään: 
cat dokumentti | päivämäärän_muotti       -> ja kaikki mikä sopii tuohon muottiin tulostetaan sillä ne ovat päivämääriä.
 

- koska regex:ät ovat likimääräisiä muotteja niillä saa myös tehtyä pikavauhtia sutta-ja-sekundaa
 
- esitetäänpä kuinka tekstiriviltä voidaan erottaa sillä olevat numerosarjat:
  echo "test+12.3e-3*§'e  eeEem..   ..eeeE lihhh-5e+5" | sed 's/e[a-zA-Z ]//g' | sed 's/\.[a-z .]//g' | grep -Po  "[0-9.e+-]*"

- regex:ät toimivat kaikkien sovellusten kanssa, joskin jokaisessa regex saattaa olla kovinkin erinäköinen, esimerkiksi edellinen  awk:illa: 
  awk 'BEGIN {muotti="[0123]?[0-9].[01]?[0-9].[20][0-9][0-9]" }; $0 ~ muotti { print }' ~/ohje

- regex:ät toimivat myös BASH:issa: pvm="31.12.2014"; echo ${pvm//[0123][0-9].[01][0-9].[20][0-9][0-9][0-9]/tämä on suomalainen paivays}

- bashissa on operattori =~ joka tarkoitettu toimimaan regex:ien kanssa. Sen käyttö: [[ $tekstijono =~ regex ]] && toiminto 
  käsky =~ asettaa muuttujia: BASH_REMATCH koko regexistä ja BASH_REMATCH[n] regex:in jokaisen sulun järjestysluvun mukaan. 

- mikäli regex:ssä on miinus-merkki täytyy sen sijaita regex:än viimeisenä merkkinä.

- regex:lle voi antaa ohjeita, esim.: (?isU) jossa i=merkkikoko isotaipieni, s=etsintä voi ulottua monelle riville, U=ungreedy ...
    
"\([a-z]\)\1"        muotti kuvaa kahteen peräkkäistä samaa pien-aakkosta. Näitä takaisinpäin viittauksia voi olla 9 - "takaisinpäinviitattava" alkaa aina \(
[muotti1\|muotti2]*  muotti kuvaa jokaiseen joka sopii muotti1:een tai muotti2:een.
muott1muotti2        muotti kuvaa jokaiseen joka sopii sekä muotti1:een että muotti2:een.; esim. [0-9][0-9] merkitsee: kaksi numeroa peräkkäin
- merkit $, *, ., [, and \ menettävät listassa erikoismerkityksensä. Kuitenkin #- ilmaisut muotoa [.ch.] ja [=a=] ovat erikseen. Myös merkinnät \n and \t tunnistetaan listoissa.
grep -Po '(?<=edessä).*(?=perässä)'  # tulostaa tekstin joka on hakuavaimien välissä

 positive-negative-"look-ahead"-"look-behind":
| grep -Po 'tätä_haetaan(?=.*tämän_täytyy_olla_perässä_mutta_tätä_ei_valita)'  # haku- ja kelpuutus-sanojen välissä saa olla tekstiä
| grep -Po 'tätä_haetaan(?!tämä_ei_saa_olla_perässä)'                          # haku- ja esto-sanojen välissä ei saa olla tekstiä
| grep -Po '(?<=tämän_täytyy_olla_edessä_mutta_tätä_ei_valita)tätä_haetaan'    # haku- ja kelpuutus-sanojen välissä ei saa olla tekstiä
| grep -Po 'tätä_haetaan(?<!tämä_ei_saa_olla_edessä)'                          # haku- ja esto-sanojen välissä saa olla tekstiä
| grep -Po '(?<=teksti_edessä).*(?=teksti_perässä)'                            # tulostaa tekstin joka on hakuavaimien välissä

haettavat voivat olla aivan mitä hyvänsä regexiä kaikissa noissa viidessä, joten voidaan kirjoittaa myös:
echo "A lvlökmgkrmgoerrkgkertgkeåthåäpthk B" |  grep -Po '(?<=^[A-Z]).*(?=[A-Z]$)'
käsky sanoin kerrottuna: jos rivi alkaa ja loppuu suurella kirjaimella niin tulosta teksti noiden suurten kirjainten välistä 

Sama hieman toisilla kilkkeillä:
echo 55 koetta | grep -Po '\d+(?= koetta)'     # tulostaa 55
echo 55 koetta | grep -Po '(?=\d+ koetta)\d+'  # tulostaa 55
echo 55 koetta | grep -Po '\d++(?! koetta)'    # tulostaa tyhjää koska tuo suluissa oleva sana:koetta  kieltää ettei saa tulostaa jos rivillä on sana: koetta

- seuraava erottaa lohkot jotka alkavat begin ja loppuvat end. begin ja end saavat olla erinriveillä.Mutta ennenkaikkea tästä ilmenee kuinka regex:lle annetaaan ohjeita (?...-ryhmissä :
i=älä välitä merkkikoosta, s=begin ja end voivat olla eri riveillä, m=merkin:^ käyttö sallitaan lukitsemaan begin rivin alkuun; joskus ^ kannattaa jättää pois
cat /boot/grub/grub.cfg | grep -Poz  '(?ism:BEGIN.*?END)'

- toinen esimerkki ohjeiden antamisesta regex:ille:
echo AAA | grep -Po '(?i)a*'
 


Regex:ille on laajennos jotka taytyy erikseen ottaa käyttöön. Pitää käyttää egrepiä, tavallisella grepillä kytkintä E ja sed:illä kytkintä: -r. Ne ovat:
*          nolla tai useampi edellistä merkkiä. Esimerkiksi a*=yksi tai lukemattomia a-kirjaimia mutta a.*=a jota seuraa nolla tai lukemattomia mekkejä 
+          yksi tai useampi edellisestä merkistä. varmempi on käyttää plussan paikalla: \{1,\}:
|          tai (or)
?          nolla tai yksi edellistä
()         sitoo merkkiryhmät yhteen. Esimerkiksi: grep -E '*(do)*(1|2)' . Kaikki merkit juuri näin
\{i\}       Täsmälleen i-kappaletta. Tulkinta: a{2\}=etsitään kahta perättäistä a:ta jonka ehdon myös aaa täyttää. echo aaa | grep -Eo a{1,2\} tulostaakin 2 riviä
\{i,j\}     Välillä i-j
\{i,\}      Jotakin yli i. siis esimerkiksi: egrep ^.\{15\}A etsii sanaa jonka alussa on 15 mielivaltaista merkkiä ja heti niiden perässä A
\{,1\}      Edellistä kohtaa on nolla tai yksi
\a{2,\}?    Jokainen a-ryhmä, jossa on vähintään 2 a:ta  (2:n paikalla voi olla mikähyvänsä luku joka toimii miniminä)
\(regexp\) esim. \(abc\)* korvaa nollaa tai suurempaa joukkoa jonka jokainen jäsen on abc, kuntaas abc* etsisi joukkoa jonka edessä on ab ja perässä on nolla tai useampi c

- regex on ahne, se valitsee aina pisimmän mikä sopii. Joskus kuitenkin halutaan erottaa erikseen kaikki sopivat. Esimerkki tästä lazy-regex:ästä:
  echo "123 abc 456 def 789" | grep -o -P [0-9].*[a-z]   # tulostaa: 123 abc 456 def 
  echo "123 abc 456 def 789" | grep -o -P [0-9].*?[a-z]  # tulostaa kaksi riviä: 123 a   ja: 456 d

Siis toisin esitettynä:
a*b          # yksi tai useampi a ja perässä yksi b
a\?b         # joko b tai ab
a\+b\+       # rivi alkaa kahdellä tai useammalla a:lla ja perässä yksi tai useampi b. 
.*           # rivillä voi olla mitätahansa      
^main.*(.*)  # rivi alkaa sanalla main ja rivillä on jossain merkit ()
^#           # rivi alkaa merkillä #
\\$          # rivi loppuu kenoon
\$           # rivi loppuu dollari-merkkiin
[a-zA-Z0-9]  # merkkijono jossa on pelkästään aakkosia ja numeroita
[^ tab]\+    # rivin alussa TAB ja sen perässä merkkijono
^\(.*\)\n\1$ # kaksi samanlaista tekstijonoa ja niiden välissä rivinvaihto
.\{9\}A$     # tekstijono jossa on yhdeksän merkkia ja sitten A rivin lopussa
^.\{15\}A    # tekstijono jonka alussa on 15 merkkiä ja sitten A
'\(.\)\(.\).\2\1' # palindromi
'\([0-9]\)\1'     # kaksi samaa merkkiä peränjälkeen

regex=[0-9]* ; [[ string =~ $regex ]] && tee_jotakin # hakasulkujen välissä ei laajenneta. 

luku=9999; [[ $(echo $luku | grep -E '^[0-9]+$') ]] && echo "kokonaisluku"
luku=.9999; [[ $(echo $luku | grep -E '^[0-9]*\.[0-9]*$') ]] && echo "desimaaliluku"
luku=-0.9e-999; [[ $(echo $luku | grep -Po  '[-+]?[0-9]*\.?[0-9]+[eE][-+]?\d+') ]] && echo "tieteelisen esitysmuodon luku"

text="aaa<hakasulkujen välissä olevaa tekstiä ei haluta tulokseen>bbb<voi hakasulkutekstejä olla useampiakin>ccc"; echo "${text//<*([^>])>/}"
grep -P '\w' # heittomerkit täytyy olla, mutta pehmeät heittomerkit kelpaavat myös
    
http://en.wikipedia.org/wiki/Regular_expression#Character_classes



- Mutta esimerkiksi päiväyksen todella pätevä regex on hirviö. Se alussa esitettyhän on ihan reikäinen, ja aikapaljon parempi on:
  echo 31.12.2001 | egrep  ^[0-2''][0-9]\.[0-9][012'']\.[0-9][0-9][0-9][0-9]$\|^3[01]\.[0-9][012'']\.[0-9][0-9][0-9][0-9]$
  mutta sekään ei ole moitteeton. Ja sekin ilmiö tulee että toimii missä linux-distrossa sattuu.

TEST="Some numbers 12345 and special char *&^%$#"; if [[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]; then echo merkit kelvollisia; else echo kelvottomia merkkejä; fi

ls [a-z]* tulostaa myös suurilla kirjaimilla alkavat, sillä kyseesä on extglob. Toimiva käskymuoto on: ls [[:lower:]]*  . ls ei käytä regexiä ?

Olen tässä ihmetellyt regex:än osumien lukumäärää rajoittavia määreitä, esimerkiksi a{2\} jonka pitäisi rajoittaa a-kirjaimien maksimimäärän kahteen. Mutta esimerkiksi: 
echo aaaa | egrep a{2\} ilmoittaa että löytyihän niitä a-kirjaimia. Mutta kyse taitaakin olla siitä, että regex:n omalta kannalta se tekee täsmälleen sen minkä lupaakin; se on vain minun tulkintani ettei neljää a:ta saa olla. Mielestäni tämä selviää kun egrep-käskyyn lisää määreen -o : echo aaaa | egrep -o a{2\}   
joka tulostaa kahdelle riville aa; siis regex hakee merkkijonoa aa ja kun löytää sen niin tulostaa sen ja jatkaa hakua ja kun löytää niin tulostaa sen ja jatkaa hakua ja kun ... 

- osoittautui että on tosivaikeaa tehdä regex joka tarkistaa että "grepattavassa" on esimerkiksi täsmälleen kaksi perättäistä samaa merkkiä ja kolme tai enemmän hylätään. Mutta seuraava viritelmä toimii:
apu=abcdefgghijklmnopqrstuvwxyz; [[ ! $(echo $apu | grep -E '(.)\1{2}') ]] && echo $apu | grep -Eo '(.)\1{1}'
- löytyy varmaankin yksinkertaisempikin regex-konsti. Mutta oikein virittämällä mitkä tahansa merkit, merkkimäärät ja regex:ät kelpaavat.
- grep  "\<[0-9]\{5\}\>" kelpaa vain silloin kun luvun molemmin puolin on välilyönti tai tekstijonon alku tai loppu.

grep -E '(.)\1'    # etsii löytyykö jokin merkki useaman kerran peräkkäin
grep -E '(.)\1{2}' # etsii löytyykö jokin merkki täsmälleen 3 kertaa peräkkäin
**
Aina oppii. Seuraavat käskyt kertovat mistä on kyse:
echo "<<tulostuu>>ei saa tulostua<<tulostuu>>" |  grep -Po '(?<=<<).*(?=>>)'      # mikäli tag-ryhmiä on samalla rivillä useampia niin tuloste on väärä
mutta:
echo "<<tulostuu>>ei saa tulostua<<tulostuu>>" |  grep -Po '(?U)(?<=<<).*(?=>>)\1{2}'  # tämä taas tulostaa oikein. Ungreedy voi kylläkin myös sotkea
tätä voi jalostaa edelleen sillä edellinen ei tulosta oikein mikäli tag:ien välinen teksti on monella rivillä. Silloin pitää kirjoittaa:
cat tiedosto |  grep -Po '(?Us)(?<=<<).*(?=>>)'                                 
- tuo << ..... >> on vai esimerkki tageista: kyllä mikä teksti hyvänsä kelpaa tag:iksi
-tällätavalla saa suoritettua vaikka seuraavankaltaista:
echo "<<a>><<b>>" |  grep -Po '(?U)(?<=<<).*(?=>>)' | [[ $(wc -l) = 2 ]] && echo 'tagiryhmiä on 2' || echo 'tagiryhmiä ei ole 2'
**
# BASH_REMATCH: 

#!/bin/bash
sana="höpinää 2013:06:23 lisää höpinää"; regexp="([0-9]+):([0-9]+):([0-9]+)"
[[ "$sana" =~ $regexp ]] && echo "sanassa seuraava palanen täytti koko regex:än-> $BASH_REMATCH; vuosi ${BASH_REMATCH[1]} tunti ${BASH_REMATCH[2]} minuutti ${BASH_REMATCH[3]}"
# siis mikäli sanassa mikään ei täytä koko regexää niin ei tulosteta mitään - elikä 2013:06 ei riitä. 
**
regex -P tuntee myös rekursion. Esimerkiksi käsky: echo aaazzz | grep -P 'a(?R)?z' tunnistaa onko lopussa yhtämonta z:aa kuin alussa on a:ta. 
**
regex tuntee myös funktiokutsut. Esimerkiksi käsky: echo habckaaajbabgbbbpcbakabt | grep -P '(?P<f_nimi>[abc])(?1)(?P>f_nimi)' tunnistaa koska tekstissä on peräkkäin kolme aakkosta ryhmästä abc.
**
käskyssä: 
echo 'aaaab aaaad' | grep -P "(?+1)(?'ryhman_nimi'[abc])(?1)(?-1)(?&ryhman_nimi)"  
(?+1)                  viittaa seuraavaan ryhmän seuraavaan jäseneen
(?-1)                  viittaa ryhmän seuraavaan jäseneen
(?1)                   viittaa koko ryhmään 
(?'ryhman_nimi'[abc])  määrittelee sen ryhmän
(?&ryhman_nimi)        on kertoo että "tässähän tämä oli"
lopputuloksena tarkistetaan onko tulevassa tekstin sanoissa viisikirjaimisia ryhmiä joissa peräkkäin vain tuon määrätyn ryhmän jäseniä.

kun sitten halutaan määrätä hyväksyttyjen merkkien luvun niin näin se käy:
echo 'aaaaaacc aaaad' | grep -P "(?+1)(?'kakkularatsis'[abcdefghij])(?1){3}(?-1)(?&kakkularatsis)"
- tuo kolme määrää että 4+3 elikä 7

tarkista löytyykö kaikille kaarisuluille pari:
echo '(lkg(mb(lk)fgf))' | grep -P '\A(\((?>[^()]|(?1))*\))\z'
**
Aloinpa etsiä BASH:issa toimivia PERL:in regex:iä: siis kaikissa niissä on tuo "grep -P". Käskyt ovat siitä kivoja että ne ovat perusteita 

tulosta kolmemerkkiset lohkot joiden kaikki merkit kuuluvat joukkoon [abc] ( määrä on siis {3}+1 )
echo jjjjaacaahhhhhbbbbbbbbhh | grep -P '([abc])(?1){3}' 
esimerkiksi tästä voi kehittää:
tulosta lohkot jotka muodostavat kolmesta perättäisestä a:sta tai neljästä perättäisestä joukon [bcd] jäsenestä
echo jjjjaaccachhhhhbccbbbbhh | grep -P '([a]{3}|[bcd])(?1){3}'

tulosta kuusikirjaimiset sanat joissa on mukana sana: cat
echo aacataa | grep -P '(?=\b\w{6}\b)\b\w*cat\w*\b'

tulosta teksti joka on ensimmäisen ja viimeisen numeron välissä - mutta tekstissä ei saa olla erikoismerkkejä
echo jjhblbk17x7yhHYH17hjjhjhblhblb | grep -P '(?=(\d(?U)))\w+\1'

mikä muu e:n sisältävä kelpaa paitsi sellainen jossa e:tä seuraa 1
echo koe | grep -P '.*e(?!1)'

tulosta samoista numeroista muodostuneet numerosarjat (tai [a-z] tai ....):
echo gjjhbljhb555567jbjhbjhb | grep -P '([0-9])\1+'

echo "23:s" | grep -P '23(:s)?'         # valitsee: 23:s koska regex on tilassa greedy
echo "23:s" | grep -P '23(:s)??'        # valitsee: 23   koska regex on tilassa lazy
echo "23:s" | grep -P '(?U)23(:s)?'     # valitsee: 23   koska regex on tilassa lazy

echo "<EM>first</EM>" | grep -P '<.+?>  # kysymysmerkki tekee lazy:n elikä valitsee vain molemmat <EM> ja: <\EM>

echo adhd | grep -P 'h\Kd'              # tulosta h:n jälkeinen d
echo adhd | grep -P '(?<=h)d'           # tulosta h:n jälkeinen d
echo adhd | grep -P 'h\K\w+'            # tulosta h:n jälkeinen teksti       



