Sommaire

Introduction

Dans l’article “Rapide introduction aux expressions rationnelles”, j’évoquais la possibilité d’utiliser des regexp avec grep.
En général c’est très utile lorsque l’on doit chercher des patterns dans des fichiers de logs, voire extraire des informations (avec cut ou awk). On va aussi utiliser plusieurs grep à travers des pipes. Un point important à noter est l’utilisation dans le premier grep de l’expression qui va extraire le moins d’information de façon à ce que les grep (ou autres commandes) suivants aient le moins de données à traiter.

Voici quelques options de grep qu’il faut connaître :

  • -i : pour ignorer la casse (A et a seront matchés de la même façon).
  • -q : mode quiet ne retourne qu’un code d’erreur que 0 ou 1 (0 => ça matche / 1 => ça ne matche pas) => à vérifier avec la variable $?.
  • -v : renvoie unique les lignes qui ne matchent pas.
  • -E : permet l’utilisation des regexp étendue.
  • -n : préfixe par le numéro de ligne qui est matchée
  • -h : ne préfixe pas par le nom du fichier suivi de :. (pratique si on cherche des patterns dans plusieurs fichiers)

Let’s go matching

voici le texte utilisé pour les différents exemples (fichier to_match):

192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255
1924168156212
coucou
email@example.com
longtextesurunelignesansespace
1234569987762662665255252
12
code erreur #4.4.2
INPUT=12
OUTPUT=12
00.12.00.3

matcher une adresse IP V4

un . matche n’importe quel caractère, il faut obligatoirement l’échapper (avec \) pour qu’il matche correctement le caractère .

grep "192.168.56.12" to_match
192.168.56.12
1924168156212

grep "192\\.168\\.56\\.12" to_match
192.168.56.12

grep '192\.168\.56\.12' to_match
192.168.56.12

grep -E '((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}(1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))' to_match
192.168.56.12
1.19.199.255
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255

grep -E '((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}(1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))(/(3[0-2]|[12]?[0-9]))?' to_match
192.168.56.12
1.19.199.255
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255

Explication de la regexp ((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}(1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))(/(3[0-2]|[12]?[0-9]))?

Elle permet de matcher n’importe quelle adresse IP V4 avec son masque de réseau en notation CIDR

On matche un octet

192.168.156.112
\_/
 +----- matching d'un octet

Expression : 1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5])

  • on va matcher entre 0 et 99 : [1-9]?[0-9]
  • on va matcher les valeurs comprise entre 100 et 199 avec cette expression : 1[0-9][0-9]
  • puis les valeurs entre 200 et 255 avec celle-ci : 2([0-4][0-9]|5[0-5])

On matche un octet suivi d’un point

192.168.156.112
\__/
 +----- matching d'un octet suivi d'un point

Expression : (1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.

On matche le groupe “un octet suivi d’un point” 3 fois

192.168.156.112
\__/\__/\__/
\__________/
      +----- matching d'un octet suivi d'un point : 3 fois

Expression : ((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}

On matche le groupe “un octet suivi d’un point” 3 fois suivi d’un octet

192.168.156.112
\__/\__/\__/\_/
\_____________/
        +----- matching d'un octet suivi d'un point 3 fois suivi d'un octet

Expression ((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}(1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))

on peut matcher de O.O.O.O à 255.255.255.255 et on matchera pas les adresses 00.00.12.3 par exemple.

Et on matche avec ou pas la notation CIDR

192.168.156.112/22
\__/\__/\__/\_/\_/
\________________/
        +----- matching d'un octet suivi d'un point 3 fois suivi d'un octet et CIDR

Expression ((1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}(1[0-9][0-9]|[1-9]?[0-9]|2([0-4][0-9]|5[0-5]))(/(3[0-2]|[12]?[0-9]))?

  • (/(3[0-2]|[12]?[0-9]))? va matcher de manière optionnelle le /22
  • on va matcher de 0 à 29 et de 30 à 32

Matcher en fonction d’un certain nombre d’occurrences

Le premier cas va matcher une série de caractères 8 à 17 caractères. Ce qui comprend bien sûr des chaînes supérieur à 17 caractères.

grep -E '.{8,17}' to_match
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
255.255.255.255
1924168156212
email@example.com
longtextesurunelignesansespace
1234569987762662665255252
code erreur #4.4.2
INPUT=12
OUTPUT=12
00.12.00.3

Le deuxième cas va matcher strictement les chaînes comprises entre 8 et 17 caractères.

grep -E '^.{8,17}$' to_match
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
255.255.255.255
1924168156212
email@example.com
INPUT=12
OUTPUT=12
00.12.00.3

Ensuite on va matcher des chaînes ayant 3 chiffres

grep -E '[[:digit:]]{3}' to_match 
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
255.255.255.255
1924168156212
1234569987762662665255252

Ensuite on va matcher des chaînes ayant de 1 à 3 chiffres suivi d’un point

grep -E '[[:digit:]]{1,3}\.' to_match 
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255
code erreur #4.4.2
00.12.00.3

Ensuite on va matcher des chaînes ayant de 1 à 3 chiffres suivi d’un point le tout 3 fois

grep -E '([[:digit:]]{1,3}\.){3}' to_match 
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255
00.12.00.3

Ensuite on va matcher des chaînes ayant de 1 à 3 chiffres suivi d’un point le tout 3 fois suivi d’une chaîne de de 1 à 3 chiffres

grep -E '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}' to_match 
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255
00.12.00.3

On est loin de matcher une adresse IP V4 correctement d’où la regexp beaucoup plus complexe vue précédemment.

quelques autres exemples :

grep -E '(626){1,2}' to_match 
1234569987762662665255252

grep -E 'r{1,2}' to_match 
longtextesurunelignesansespace
code erreur #4.4.2

grep -E '(626){2}.(525){1,2}' to_match 
1234569987762662665255252

Groupes et ou

Il est intéressant aussi de voir qu’on va pouvoir matcher un pattern ou un autre et groupe tout cela.

grep -E '(IN|OUT)PUT' to_match 
INPUT=12
OUTPUT=12
grep -E '192\.168\.(1|2)\.' to_match 
192.168.2.3/1
192.168.1.1/32
grep -E '^192\.168\.(1|2)\.|(^(1|0)\.)' to_match 
1.19.199.255
192.168.2.3/1
192.168.1.1/32
0.0.0.0
grep -E 'OUTPUT|erreur' to_match 
code erreur #4.4.2
OUTPUT=12
grep -E '^([a-zA-Z]+|[^\.]+)$' to_match 
1924168156212
coucou
longtextesurunelignesansespace
1234569987762662665255252
12
INPUT=12
OUTPUT=12

Autres exemples

Je matche des chaînes contenant des lettres.

grep -E '[[:alpha:]]' to_match 
coucou
email@example.com
longtextesurunelignesansespace
code erreur #4.4.2
INPUT=12
OUTPUT=12

Je matche des chaînes contenant uniquement des lettres.

grep -E '^[[:alpha:]]+$' to_match 
coucou
longtextesurunelignesansespace

Je matche des chaînes contenant des chiffres.

grep -E '[[:digit:]]' to_match 
192.168.56.12
1.19.199.255
256.256.256.256
192.168.249.239
192.168.249.0
192.168.12.13/22
192.168.2.3/1
192.168.1.1/32
0.0.0.0
255.255.255.255
1924168156212
1234569987762662665255252
12
code erreur #4.4.2
INPUT=12
OUTPUT=12
00.12.00.3

Je matche des chaînes contenant uniquement des chiffres.

grep -E '^[[:digit:]]+$' to_match 
1924168156212
1234569987762662665255252
12

Je matche des chaînes contenant aucun chiffre.

grep -E '^[^0-9]+$' to_match 
coucou
email@example.com
longtextesurunelignesansespace

Idem.

grep -E '^[^[:digit:]]+$' to_match 
coucou
email@example.com
longtextesurunelignesansespace

Je matche une adresse e-mail (attention ceci est une version ultra simplifiée, la regexp est beaucoup plus compliquée que cela cf. la RFC 3696)

grep -Ei '^([a-z0-9\-\.\+_]+)@([a-z0-9\-\.]+\.[a-z]+)' to_match 
email@example.com

Exemple avec un pipe et un grep excluant des chaînes matchées (option -v).
Je matche des chaînes contenant aucun chiffre et j’enlève toute les chaînes contant @.

grep -E '^[^[:digit:]]+$' to_match | grep -v '@'
coucou
longtextesurunelignesansespace

Quelques liens intéressants sur les regexp