Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 914 minDébutant

Expressions régulières : -match, Select-String, grep et sed

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • lire et écrire une expression régulière simple et ancrée ;
  • valider, extraire et remplacer avec -match, -replace et Select-String ;
  • chercher dans des journaux avec grep, sed et [[ =~ ]] ;
  • compter les tentatives de connexion échouées par adresse.

4 exercices corrigés

Une expression régulière (regex) est un motif qui décrit une famille de chaînes de caractères : « PC- suivi de quatre chiffres », « une adresse IPv4 », « une ligne qui contient Failed password ». Elle sert à vérifier une saisie, à chercher des lignes dans un journal et à extraire ou remplacer une partie d'un texte. PowerShell (avec -match, -replace, Select-String) et Bash (avec grep, sed et [[ =~ ]]) utilisent la même syntaxe de base, avec quelques différences que ce chapitre signale.

La syntaxe de base

MotifCorrespond àExemple qui correspond
abcLa suite de caractères abc, n'importe oùsrv-abc-01
.N'importe quel caractère (un seul)s.v : srv, s-v
\.Un point, au sens propre (le \ retire le sens spécial)ad\.sorbier
[abc], [a-z], [0-9]Un caractère de l'ensemble ou de l'intervallePC-[0-9] : PC-4
[^0-9]Un caractère qui n'est pas dans l'ensemble
\d, \w, \sUn chiffre, un caractère de mot (lettre, chiffre, _), un espace\d\d : 42
^, $Début, fin de la chaîne (ou de la ligne)^srv- : commence par srv-
*, +, ?0 fois ou plus, 1 fois ou plus, 0 ou 1 fois l'élément précédentab+c : abc, abbbc
{4}, {2,5}, {2,}Exactement 4 fois, de 2 à 5 fois, au moins 2 fois\d{4} : 2026
(…)Un groupe, que l'on peut répéter ou récupérer(ab)+ : ababab
a|ba ou b(Failed|Accepted)
\bUne frontière de mot\broot\b ne trouve pas rootkit

Ancre ton motif dès que tu valides une saisie : \d{4} trouve quatre chiffres dans PC-004212, alors que ^PC-\d{4}$ exige que la chaîne entière soit PC- suivi d'exactement quatre chiffres.

Les quantificateurs sont gourmands : ".*" sur le texte "a" et "b" prend "a" et "b" en entier, du premier au dernier guillemet. Ajoute ? pour la version paresseuse, qui s'arrête au plus tôt : ".*?" prend "a". La version paresseuse existe en PowerShell et avec grep -P, pas dans les expressions étendues de grep -E et sed -E.

Vérifier avec -match en PowerShell

-match renvoie $true ou $false et, quand la gauche est une seule chaîne, remplit la variable automatique $Matches avec ce qui a été trouvé.

powershell
'PC-0042' -match '^PC-\d{4}$'        # True
'pc-0042' -match '^PC-\d{4}$'        # True : -match ignore la casse
'pc-0042' -cmatch '^PC-\d{4}$'       # False : -cmatch la respecte
'PC-00421' -match '^PC-\d{4}$'       # False : cinq chiffres

'srv-fichiers.ad.sorbier.example' -match '^(?<hote>[^.]+)\.(?<domaine>.+)$'
$Matches.hote                         # srv-fichiers
$Matches.domaine                      # ad.sorbier.example
$Matches[0]                           # srv-fichiers.ad.sorbier.example : toute la correspondance

(?<nom>…) est un groupe nommé : son contenu se lit dans $Matches.nom. Sans nom, les groupes sont numérotés $Matches[1], $Matches[2] dans l'ordre de leurs parenthèses ouvrantes.

Quand la gauche est un tableau, -match filtre : 'srv-ad', 'srv-web', 'srv-ad2' -match '^srv-ad' renvoie srv-ad et srv-ad2, et ne remplit pas $Matches.

Remplacer et découper

-replace remplace tout ce qui correspond. Dans le texte de remplacement, $1, $2 ou ${nom} reprennent les groupes. Écris ce texte entre apostrophes, sinon PowerShell remplace lui-même $1 par une variable vide avant que la regex ne s'en serve.

powershell
'Lefèvre Julie' -replace '^(\w+) (\w+)$', '$2 $1'           # Julie Lefèvre
'192.168.10.177' -replace '\.\d+$', '.0'                    # 192.168.10.0
'Facture   du  3/11' -replace '\s+', ' '                    # Facture du 3/11
'srv-ad, srv-ad2 ;srv-web' -split '\s*[,;]\s*'              # srv-ad, srv-ad2, srv-web

\w reconnaît les lettres accentuées en .NET : Lefèvre est bien un mot. Ce n'est pas le cas partout : en Bash, l'équivalent dépend des réglages de langue du système.

Chercher dans des fichiers avec Select-String

Select-String est le grep de PowerShell : il lit des fichiers (ou des chaînes reçues du pipeline) et renvoie les lignes qui correspondent, avec le nom du fichier, le numéro de ligne et les correspondances.

Voici un extrait d'un journal SSH exporté de srv-web, auth.log :

text
2026-11-03T02:14:07+01:00 srv-web sshd[4121]: Failed password for invalid user admin from 203.0.113.50 port 51234 ssh2
2026-11-03T02:14:11+01:00 srv-web sshd[4121]: Failed password for invalid user admin from 203.0.113.50 port 51240 ssh2
2026-11-03T02:15:02+01:00 srv-web sshd[4130]: Failed password for root from 198.51.100.23 port 40022 ssh2
2026-11-03T02:15:09+01:00 srv-web sshd[4130]: Failed password for invalid user test from 203.0.113.50 port 51302 ssh2
2026-11-03T07:58:40+01:00 srv-web sshd[5012]: Accepted publickey for mdiallo from 192.168.10.209 port 52114 ssh2: ED25519 SHA256:k3J9
2026-11-03T08:03:15+01:00 srv-web sshd[5077]: Failed password for mdiallo from 192.168.10.209 port 52190 ssh2
2026-11-03T09:41:52+01:00 srv-web sshd[5230]: Failed password for invalid user oracle from 198.51.100.23 port 40188 ssh2

Les adresses 203.0.113.x et 198.51.100.x sont réservées à la documentation : elles jouent ici le rôle d'adresses Internet. Compter les échecs par adresse source :

powershell
$motif = 'Failed password for (invalid user )?(?<compte>\S+) from (?<ip>[\d.]+)'
Select-String -Path .\auth.log -Pattern $motif |
    ForEach-Object { $_.Matches[0].Groups['ip'].Value } |
    Group-Object | Sort-Object Count -Descending | Select-Object Count, Name
text
Count Name
----- ----
    3 203.0.113.50
    2 198.51.100.23
    1 192.168.10.209

Chaque résultat de Select-String a une propriété Matches, dont les Groups donnent les groupes nommés. -SimpleMatch cherche un texte exact sans regex, -NotMatch inverse la recherche, -CaseSensitive respecte la casse, -Context 2 affiche deux lignes avant et après chaque correspondance.

Sur un serveur Windows, les échecs de connexion ne sont pas dans un fichier texte mais dans le journal de sécurité (événement 4625), que l'on lit avec Get-WinEvent : la regex sert alors à extraire des valeurs du champ Message. L'analyse des journaux de sécurité à grande échelle est le travail d'un SIEM comme Wazuh.

grep, sed et [[ =~ ]] en Bash

grep affiche les lignes qui correspondent à un motif. Ses options les plus utiles :

OptionEffet
-EExpressions régulières étendues : +, ?, {n}, | et les parenthèses sans \ devant
-PExpressions compatibles Perl (\d, quantificateurs paresseux), propre à GNU grep
-iIgnorer la casse
-vInverser : les lignes qui ne correspondent pas
-cCompter les lignes qui correspondent
-oN'afficher que la partie qui correspond, une par ligne
-nNuméroter les lignes
-rChercher dans tous les fichiers d'un dossier, récursivement
-qNe rien afficher, seulement le code de retour (pour if)

Le même comptage par adresse qu'en PowerShell :

bash
grep "Failed password" auth.log | grep -oE 'from ([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d' ' -f2 | sort | uniq -c | sort -rn
text
      3 203.0.113.50
      2 198.51.100.23
      1 192.168.10.209

Les comptes inexistants que les attaquants ont essayés, sans doublons :

bash
grep -oE 'invalid user [^ ]+' auth.log | cut -d' ' -f3 | sort -u
text
admin
oracle
test

Sur Debian 13, le journal d'authentification n'est plus un fichier /var/log/auth.log par défaut : il est dans le journal du système, que l'on lit avec journalctl -u ssh et que l'on filtre avec les mêmes grep. C'est la pratique du chapitre 13.

sed modifie un flux de texte ; sa commande s/motif/remplacement/ remplace, et \1, \2 reprennent les groupes. Avec -E, les parenthèses forment des groupes sans \.

bash
sed -E 's/^([0-9-]+)T([0-9:]+)\+01:00 ([^ ]+) .*from ([0-9.]+).*/\1 \2 \3 \4/' auth.log | head -2
text
2026-11-03 02:14:07 srv-web 203.0.113.50
2026-11-03 02:14:11 srv-web 203.0.113.50

sed -i modifie un fichier sur place : sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config. Garde une copie avant (sed -i.bak … crée sshd_config.bak), et vérifie le résultat : une regex trop large modifie des lignes que tu ne voulais pas toucher.

Dans un script, [[ chaîne =~ regex ]] teste une regex étendue et range les groupes dans le tableau BASH_REMATCH :

bash
ligne='Failed password for root from 198.51.100.23 port 40022 ssh2'
motif='for (invalid user )?([^ ]+) from ([0-9.]+)'
if [[ $ligne =~ $motif ]]; then
    echo "compte=${BASH_REMATCH[2]} ip=${BASH_REMATCH[3]}"
fi
text
compte=root ip=198.51.100.23

Range toujours le motif dans une variable et écris $motif sans guillemets à droite de =~ : entre guillemets, Bash chercherait le texte littéral et non la regex.

Des motifs prêts à l'emploi

Pour reconnaîtreMotif (ancré)Remarque
Un login Sorbier (initiale et nom, chiffre facultatif)^[a-z]{2,20}[0-9]?$Minuscules sans accents, comme la convention de nommage
Un numéro d'inventaire^PC-[0-9]{4}$
Une adresse MAC^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$Accepte : et -
Une adresse IPv4 (forme)^([0-9]{1,3}\.){3}[0-9]{1,3}$Accepte 999.1.1.1 : vérifie ensuite chaque octet ≤ 255, ou utilise [ipaddress] en PowerShell
Une date au format ISO^[0-9]{4}-[0-9]{2}-[0-9]{2}$La forme seulement : 2026-13-45 passe
Une ligne de commentaire ou vide^\s*(#|$)Pour sauter ces lignes dans un fichier de configuration

Exercices

Exercice 1 sur 4 Écrire le motif Réussi À revoir

Écris l'expression régulière, ancrée, qui reconnaît :

  1. un nom de serveur Sorbier : srv- suivi de lettres minuscules, et éventuellement d'un chiffre final (srv-ad, srv-ad2, srv-fichiers) ;
  2. un nom de compte d'administration : adm, un chiffre de 0 à 2, un tiret, puis un login (adm0-amercier) ;
  3. une ligne de journal pfSense ou Linux qui contient le mot block ou le mot deny, quelle que soit la casse (en PowerShell).
Voir la réponseMasquer la réponse de l'exercice 1
  1. ^srv-[a-z]+[0-9]?$
  2. ^adm[0-2]-[a-z]{2,20}[0-9]?$
  3. '\b(block|deny)\b' avec -match, qui ignore déjà la casse : $ligne -match '\b(block|deny)\b'. Pas d'ancre ici : on cherche le mot n'importe où dans la ligne. \b évite de trouver blocked ou denying ; si tu veux aussi ces formes, retire les \b.

Tu avais trouvé ?

Exercice 2 sur 4 Prédire la sortie Réussi À revoir

Donne ce qu'affiche chaque ligne PowerShell.

powershell
'PC-0042' -match '\d{4}'
'PC-004212' -match '^PC-\d{4}$'
'srv-web' -replace 'web', 'bdd'
'Diallo Moussa' -replace '^(\w+) (\w+)$', '$2.$1'
'a1b22c333' -replace '\d+', '#'
('srv-ad;srv-ad2;;srv-web' -split ';').Count
Voir la réponseMasquer la réponse de l'exercice 2
text
True
False
srv-bdd
Moussa.Diallo
a#b#c#
4

La première ligne est vraie car le motif n'est pas ancré : 0042 se trouve dans la chaîne. La deuxième est fausse : ^…$ exige exactement quatre chiffres. \d+ prend chaque suite de chiffres en entier, d'où un seul # par suite. Les deux points-virgules collés produisent un élément vide : srv-ad, srv-ad2, une chaîne vide et srv-web, soit 4 éléments.

Tu avais trouvé ?

Exercice 3 sur 4 Analyser le journal SSH Réussi À revoir

Avec le fichier auth.log de ce chapitre, écris les commandes Bash qui :

  1. comptent les échecs de mot de passe ;
  2. affichent la ligne de la connexion réussie ;
  3. affichent les comptes visés par les échecs, sans le « invalid user », triés et sans doublons.

Donne la sortie de chaque commande. Que conclus-tu sur les adresses et sur le compte mdiallo ?

Voir la réponseMasquer la réponse de l'exercice 3
bash
grep -c "Failed password" auth.log
grep "Accepted" auth.log
grep "Failed password" auth.log | sed -E 's/.*Failed password for (invalid user )?([^ ]+) from.*/\2/' | sort -u
  1. 6
  2. La ligne de 07:58:40 : … Accepted publickey for mdiallo from 192.168.10.209 port 52114 ssh2: ED25519 SHA256:k3J9.
  3. Cinq comptes :
text
admin
mdiallo
oracle
root
test

Les adresses 203.0.113.50 et 198.51.100.23, extérieures, essaient des comptes génériques (admin, root, test, oracle) : c'est une attaque par force brute ou par dictionnaire, typique d'un serveur SSH exposé. L'échec de mdiallo vient du poste d'administration (192.168.10.209), quelques minutes après une connexion réussie par clé : sans doute une erreur de saisie, à vérifier avec lui. La vraie correction est en amont : SSH ne devrait pas être joignable depuis Internet, seulement depuis le VLAN d'administration, avec une authentification par clé et Fail2ban (cours Cybersécurité des infrastructures).

Tu avais trouvé ?

Exercice 4 sur 4 Valider des saisies en Bash Réussi À revoir

Écris une boucle qui teste les valeurs PC-0042, PC-42, pc-0042 et PC-00421 avec [[ =~ ]] et le motif des numéros d'inventaire, et affiche « valide » ou « invalide » pour chacune. Donne la sortie.

Voir la réponseMasquer la réponse de l'exercice 4
bash
motif='^PC-[0-9]{4}$'
for n in PC-0042 PC-42 pc-0042 PC-00421; do
    if [[ $n =~ $motif ]]; then echo "$n : valide"; else echo "$n : invalide"; fi
done
text
PC-0042 : valide
PC-42 : invalide
pc-0042 : invalide
PC-00421 : invalide

Contrairement à -match en PowerShell, =~ respecte la casse : pc-0042 est refusé. Pour ignorer la casse en Bash, on active shopt -s nocasematch avant le test.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Appeler une API REST : Invoke-RestMethod, curl et jqRevoir le sommaire du cours