Chapitre 4. Expressions régulières

Table des matières

1. Expressions régulières
1.1. Qu'est-ce qu'une expression régulière?
1.2. Les métacaractères des expressions régulières
1.3. Expressions régulières basiques versus celles étendues
2. Exemples en utilisant grep
2.1. Qu'est-ce que grep?
2.2. Grep et les expressions régulières
3. La correspondance de patron dans les fonctionnalités Bash
3.1. Intervalle de caractère
3.2. Classes de caractères
4. Résumé
5. Exercices

Résumé

Dans ce chapitre nous abordons:

  • L'utilisation des expressions régulières

  • Les métacaractères des expressions régulières

  • Trouver des patrons dans les fichiers et autres résultats

  • Les intervalles de caractères et les classes en Bash

1. Expressions régulières

1.1. Qu'est-ce qu'une expression régulière?

Une expression régulière est un patron qui recouvre un ensemble de chaînes de caractères. Les expressions régulières sont construites de façon analogique aux expressions arithmétiques par l'emploi de diverses opérateurs qui combinent d'autres expressions réduites.

Les briques de base sont les expressions régulières qui correspondent à un seul caractère. La plupart des caractères, incluant toutes les lettres et les chiffres, sont des expressions régulières qui correspondent exactement à elle même. Tout métacaractère portant un sens spécial peut être protégé en le précédant d'un slash inversé.

1.2. Les métacaractères des expressions régulières

Une expression régulière peut être suivi par un ou plusieurs opérateurs de répétition (métacaractère):

Tableau 4.1. Opérateurs d'expression régulière

OpérateurEffet
.Correspond a tout caractère
?L'élément précédent est optionnel et sera présent au plus une fois.
*L'élément précédent sera présent zéro fois ou plus.
+L'élément précédent sera présent une fois ou plus.
{N}L'élément précédent sera présent exactement N fois.
{N,}L'élément précédent sera présent N ou plus de fois.
{N,M}L'élément précédent sera présent au moins N fois, mais pas plus de M fois.
-représente l'intervalle si il n'est pas le premier ou le dernier dans une liste
^Correspond à une chaîne vide au début de la ligne; aussi représente les caractères ne se trouvant pas dans l'intervalle d'une liste.
$Correspond à la chaîne vide à la fin d'une ligne.
\bCorrespond à la chaîne vide au début ou à la fin d'un mot.
\BCorrespond à la chaîne vide à l'intérieur d'un mot.
\<Correspond à la chaîne vide au début d'un mot.
\>Correspond à la chaîne vide à la fin d'un mot.

2 expressions régulières peuvent être concaténées; l'expression régulière résultant correspond à toute chaîne formée par 2 sous-chaînes concaténées qui respectivement correspondent aux sous-expressions.

2 expressions régulières peuvent être jointes par l'opérateur « | »; l'expression régulière résultant correspond à toute chaîne qui correspond à l'une ou l'autre des sous-expressions.

La répétition a la préséance sur la concaténation, laquelle a la préséance sur la jointure. Toute une expression peut être mise entre parenthèses pour éviter ces règles de préséance.

1.3. Expressions régulières basiques versus celles étendues

Dans les expressions régulières basiques les métacaractères « ? », « + », « { », « | », « ( », et « ) » perdent leur sens spécial; à la place employez la version avec slash inversé « \? », « \+ », « \{ », « \| », « \( », et « \) ».

Vérifiez dans la documentation de votre système si les commandes admettent les expressions étendues dans les expressions régulières.

2. Exemples en utilisant grep

2.1. Qu'est-ce que grep?

grep cherche dans les fichiers en entrée les lignes qui contiennent une correspondance dans une liste donnée de patrons. Quand il trouve une correspondance dans une ligne, il copie la ligne sur la sortie standard (par défaut), ou sur tout autre sorte de sortie que vous avez requis par les options.

Bien que grep s'attende à établir une correspondance sur du texte, il n'a pas de limite sur la longueur de la ligne lue autre que celle de la mémoire disponible, et il trouve la correspondance sur n'importe quel caractère dans la ligne. Si le dernier byte d'un fichier en entrée n'est pas un saut de ligne, grep discrètement en ajoute un. Parce que le saut de ligne est aussi un séparateur dans la liste des patrons, il n'y a pas moyen de repérer les caractères saut de ligne dans le texte.

Quelques exemples:

cathy ~> grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

cathy ~> grep -n root /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
12:operator:x:11:0:operator:/root:/sbin/nologin

cathy ~> grep -v bash /etc/passwd | grep -v nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
news:x:9:13:news:/var/spool/news:
mailnull:x:47:47::/var/spool/mqueue:/dev/null
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
rpc:x:32:32:Portmapper RPC user:/:/bin/false
nscd:x:28:28:NSCD Daemon:/:/bin/false
named:x:25:25:Named:/var/named:/bin/false
squid:x:23:23::/var/spool/squid:/dev/null
ldap:x:55:55:LDAP User:/var/lib/ldap:/bin/false
apache:x:48:48:Apache:/var/www:/bin/false

cathy ~> grep -c false /etc/passwd
7

cathy ~> grep -i ps ~/.bash* | grep -v history
/home/cathy/.bashrc:PS1="\[\033[1;44m\]$USER is in \w\[\033[0m\] "

Avec la première commande, l'utilisateur cathy affiche les lignes de /etc/passwd contenant la chaîne root.

Puis elle affiche le numéro des lignes contenant cette chaîne.

Avec la troisième commande elle vérifie quels utilisateurs n'utilisent pas bash, mais les comptes avec nologin ne sont pas affichés.

Puis elle compte le nombre de comptes qui ont /bin/false comme Shell.

La dernière commande affiche les lignes de tous les fichiers dans son répertoire racine qui commencent par ~/.bash, excluant les correspondances avec history, afin d'exclure des correspondances de ~/.bash_history qui pourrait contenir la même chaîne en majuscule et minuscule.

Maintenant voyons ce que nous pouvons faire d'autre avec grep et des expressions régulières.

2.2. Grep et les expressions régulières

Si vous n'êtes pas sous Linux

Nous utilisons GNU grep dans ces exemples, parce qu'il admet les expressions régulières étendues. GNU grep se trouve par défaut sur les systèmes Linux. Si vous travaillez sur un système propriétaire, vérifiez avec l'option -V la version utilisée. GNU grep peut être téléchargé depuis http://gnu.org/directory/.

2.2.1. Ancres de lignes et de mots

A partir de l'exemple précédent, nous voulons maintenant afficher que les lignes commençant par la chaîne « root »:

cathy ~> grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash

Si nous voulons voir quels comptes n'a aucun Shell de défini, nous cherchons les lignes finissant par « : »:

cathy ~> grep :$ /etc/passwd
news:x:9:13:news:/var/spool/news:

Pour vérifier que PATH est exporté dans ~/.bashrc, d'abord sélectionner les lignes « export » puis chercher les lignes commençant avec la chaîne « PATH », afin de ne pas afficher MANPATH et autres chaînes possibles:

cathy ~> grep export ~/.bashrc | grep '\<PATH'
  export PATH="/bin:/usr/lib/mh:/lib:/usr/bin:/usr/local/bin:/usr/ucb:/usr/dbin:$PATH"

Pareillement, \> cherche à la fin d'un mot.

Si vous voulez trouver une chaîne qui est un mot isolé (entre espaces), c'est mieux d'employer -w, comme dans cet exemple où nous affichons des informations de la partition root:

cathy ~> grep -w / /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1

Si cette option est absente, toutes les lignes de la table du système de fichiers sera affichée.

2.2.2. Classes de caractères

Une expression entre crochet est une liste de caractère entre « [ » et « ] ». Elle fera correspondre tout caractère présent dans cette liste; si le premier caractère de la liste est le caret, « ^ », alors elle fera correspondre tout caractère NON présent dans la liste. Par exemple, l'expression régulière « [0123456789] » repère n'importe quelle chiffre.

Dans une expression entre crochet, une expression intervalle consiste en 2 caractères séparés par un tiret. Elle repère tout caractère qui se retrouve dans l'intervalle des 2 caractères inclus, employant pour ce faire la séquence du jeux de caractères définit localement. Par exemple, dans le jeux particulier par défaut en C, « [a-d] » est équivalent à « [abcd] ». la plupart des jeux sont ordonnés selon l'ordre du dictionnaire, et dans ces jeux « [a-d] » est classiquement pas équivalent à « [abcd] »; cela peut être équivalent à « [aBbCcDd] », par exemple. Pour obtenir l'interprétation classique des expressions entre crochets, vous pouvez employer le jeux de C en déclarant la variable d'environnement LC_ALL à la valeur « C ».

Pour finir, des jeux de caractères nommés sont prédéfinis dans des expressions entre crochets. Voir les pages man ou info de grep pour plus d'informations au sujet de ces expressions prédéfinies.

cathy ~> grep [yf] /etc/group
sys:x:3:root,bin,adm
tty:x:5:
mail:x:12:mail,postfix
ftp:x:50:
nobody:x:99:
floppy:x:19:
xfs:x:43:
nfsnobody:x:65534:
postfix:x:89:

cathy ~> ls *[1-9].xml
app1.xml  chap1.xml  chap2.xml  chap3.xml  chap4.xml

Dans cet exemple, toutes les lignes contenant soit « y » soit « f » sont affichées, suivi par un exemple avec un intervalle employé dans la commande ls.

2.2.3. Jokers

Employez le « . » pour cibler la correspondance avec un seul caractère. Si vous voulez obtenir une liste de tous les mots d'un dictionnaire anglais de 5 caractères et commençant par « c » et finissant par « h » (pratique pour les mots-croisés ):

cathy ~> grep '\<c...h\>' /usr/share/dict/words
catch
clash
cloth
coach
couch
cough
crash
crush

Si vous voulez afficher les lignes contenant le caractère littéral point, employez l'option -F de grep.

Pour cibler plusieurs caractères, employez l'astérisque. Cet exemple cible tous les mots commençant par « c » et finissant par « h » dans le dictionnaire du système:

cathy ~> grep '\<c.*h\>' /usr/share/dict/words
caliph
cash
catch
cheesecloth
cheetah
--output omitted--

Si vous voulez cibler le caractère astérisque littéral dans un fichier ou un résultat, employez grep -F:

cathy ~> grep * /etc/profile

cathy ~> grep -F '*' /etc/profile
for i in /etc/profile.d/*.sh ; do

3. La correspondance de patron dans les fonctionnalités Bash

3.1. Intervalle de caractère

En plus de grep et des expressions régulières, il y a un bon paquet de correspondances que vous pouvez faire directement dans le Shell, sans avoir besoin d'un programme externe.

Comme vous savez déjà, l'astérisque (*) et le point d'interrogation (?) cible toute chaîne ou tout caractère, respectivement. Protégez ces caractères spéciaux pour les cibler littéralement:

cathy ~> touch "*"

cathy ~> ls "*"
*

Mais vous pouvez aussi employer les crochets pour cibler tout caractère compris ou intervalle de caractères, si la paire de caractères est séparée par un tiret. Un exemple:

cathy ~> ls -ld [a-cx-z]*
drwxr-xr-x    2 cathy	 cathy		4096 Jul 20  2002 app-defaults/
drwxrwxr-x    4 cathy    cathy          4096 May 25  2002 arabic/
drwxrwxr-x    2 cathy    cathy          4096 Mar  4 18:30 bin/
drwxr-xr-x    7 cathy    cathy          4096 Sep  2  2001 crossover/
drwxrwxr-x    3 cathy    cathy          4096 Mar 22  2002 xml/

Ceci liste tous les fichiers dans le répertoire racine de cathy, commençant par « a », « b », « c », « x », « y » ou « z ».

Si le premier caractère entre crochet est « ! » ou « ^ », tout caractère non inclus sera ciblé. Pour cibler le (« - »), l'inclure en premier ou dernier caractère de l'ensemble. L'ordre dépend du paramétrage local et de la valeur de la variable LC_COLLATE, si elle est définie. Rappelez-vous que d'autres paramétrages pourraient interpréter « [a-cx-z] » comme « [aBbCcXxYyZz] » si le tri est fait selon l'ordre du dictionnaire. Si vous voulez être sûr d'avoir l'interprétation classique des intervalles, forcer ce paramétrage en définissant LC_COLLATE ou LC_ALL à « C ».

3.2. Classes de caractères

Les jeux de caractères peuvent être spécifiés entre crochets, avec la syntaxe [:CLASS:], où CLASS est une classe définie dans le standard POSIX et a une des valeurs

« alnum », « alpha », « ascii », « blank », « cntrl », « digit », « graph », « lower », « print », « punct », « space », « upper », « word » or « xdigit ».

Quelques exemples:

cathy ~> ls -ld [[:digit:]]*
drwxrwxr-x    2 cathy	cathy		4096 Apr 20 13:45 2/

cathy ~> ls -ld [[:upper:]]*
drwxrwxr--    3 cathy   cathy           4096 Sep 30  2001 Nautilus/
drwxrwxr-x    4 cathy   cathy           4096 Jul 11  2002 OpenOffice.org1.0/
-rw-rw-r--    1 cathy   cathy         997376 Apr 18 15:39 Schedule.sdc

Quand l'option Shell extglob est activée (par l'intégrée shopt), plusieurs autres opérateurs de correspondance sont reconnus. Plus dans les pages Bash info, section Basic shell featuresShell ExpansionsFilename ExpansionPattern Matching.

4. Résumé

Les expressions régulières sont puissantes pour extraire des lignes particulières d'un fichier ou d'un résultat. Beaucoup de commandes UNIX emploient des expressions régulières: vim, perl, la base de données PostgreSQL etc. Elles peuvent être interprétées par tout langage et application par l'emploi de bibliothèques externes, et on les retrouve même sur des systèmes non-UNIX. Par exemple, les expressions régulières sont employées dans le tableur Excel qui est fourni avec la suite Office de Microsoft. Dans ce chapitre nous avons eu un avant-goût de la commande grep qui est indispensable à tout environnement UNIX.

Note

La commande grep peut faire bien plus que les quelques opérations vues ici; nous l'avons juste utilisée à titre d'illustration pour les expressions régulières. La version GNU de grep est fourni avec un bon lot de documentations, qu'il vous est recommandé de lire!

Bash a des fonctionnalités intégrées pour cibler des patrons et peut reconnaître des classes de caractères et des intervalles.

5. Exercices

Ces exercices vous aideront à maîtriser les expressions régulières.

  1. Afficher une liste de tous les utilisateurs de votre système qui se connectent avec le Shell Bash par défaut.

  2. Depuis le répertoire /etc/group, afficher toutes les lignes commençant avec la chaîne « daemon ».

  3. Imprimer toutes les lignes de ce même fichier qui ne contiennent par la chaîne.

  4. Afficher les informations du système local à partir du fichier /etc/hosts, afficher le numéro des lignes qui correspondent à la chaîne recherchée et compter le nombre d'occurrences de cette chaîne.

  5. Afficher une liste des sous-répertoires de /usr/share/doc contenant des informations au sujet des Shell.

  6. Combien de fichiers README ces sous-répertoires contiennent-ils? Ne pas prendre en compte les fichiers de la forme « README.a_string ».

  7. Faire une liste des fichiers du répertoire racine qui ont changés moins de 10 heures auparavant, en employant grep, mais sans prendre les répertoires.

  8. Mettre ces commandes dans un script Shell qui générera un résultat lisible.

  9. Pouvez-vous trouver une alternative à wc -l, avec grep?

  10. Avec la table des fichiers du système (/etc/fstab par exemple), lister les disques locaux.

  11. Faire un script qui contrôle si un utilisateur apparaît dans /etc/passwd. Pour cette fois-ci, vous pouvez spécifier le nom dans le script, vous n'êtes pas tenu de travailler avec des paramètres et des conditions à ce stade.

  12. Afficher les fichiers de configuration de /etc qui contiennent des chiffres dans leur nom.