Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Par mis à jour le

Améliorer cette page

sed est l'outil qu'on utilise quand on ne veut pas ouvrir un éditeur. Vous avez 50 fichiers de configuration à mettre à jour, un mot de passe à masquer, un hostname à changer de prod à staging sur tous les fichiers d'un dossier, sed fait ça en une ligne, de façon scriptable et répétable.

Il sert directement l'objectif RHCSA « Create and edit text files », et prolonge « Use grep and regular expressions to analyze text ». Le référentiel LFCS actuel ne le nomme pas, mais ses épreuves pratiques demandent sans cesse ce genre de modification de configuration.

  • Substituer du texte avec la commande s/avant/après/ et ses variantes
  • Modifier un fichier directement sur disque avec sed -i sans perdre l'original
  • Cibler des lignes précises avec des adresses numériques ou des patterns regex
  • Supprimer des lignes, des commentaires et des lignes vides
  • Enchaîner plusieurs transformations en une seule commande avec -e
  • Réorganiser des champs avec des groupes de capture \1, \2
  • Appliquer un workflow complet de migration de fichier de config

sed est l'outil par défaut dès que vous devez transformer du texte de façon automatisable et non interactive :

  • changer l'URL d'une base de données dans db.conf lors d'une migration prod → staging
  • masquer un mot de passe ou une clé API dans des fichiers de config avant de les versionner
  • supprimer les commentaires et lignes vides d'un fichier de configuration pour le passer à un parser
  • normaliser les espaces autour des = dans des dizaines de fichiers .ini ou .conf
  • extraire uniquement les lignes d'erreur d'un log pour alimenter un rapport

Dans un script de déploiement, sed -i est souvent la première solution : simple, sans dépendance, disponible sur tout système Linux. Dans un playbook Ansible, préférez les modules lineinfile et replace, qui ne modifient le fichier que si nécessaire et le signalent : un sed -i lancé par Ansible réécrit le fichier à chaque passage, sans dire s'il a changé quelque chose.

Toutes les sorties de cette page ont été mesurées avec GNU sed 4.9 sur Debian 13, à partir des trois fichiers ci-dessous. Créez-les dans un répertoire de travail pour reproduire chaque commande. hosts.old imite un fichier /etc/hosts d'environnement de production, db.conf une configuration aux espacements irréguliers, et access.log un extrait de journal web.

Fenêtre de terminal
cat > hosts.old <<'EOF'
192.168.1.10 web01.prod.example.com web01
192.168.1.11 web02.prod.example.com web02
192.168.1.20 db01.prod.example.com db01
10.0.0.5 bastion.prod.example.com bastion
EOF
cat > db.conf <<'EOF'
# Configuration base de données
host = db.internal.example.com
port = 5432
user=admin
password= secret123
database=production
max_connections = 100
# timeout en secondes
connect_timeout= 30
EOF
cat > access.log <<'EOF'
192.168.1.10 - alice [01/Apr/2026:08:15:02 +0000] "GET /index.html HTTP/1.1" 200 1043
10.0.0.5 - bob [01/Apr/2026:08:16:44 +0000] "POST /login HTTP/1.1" 401 219
192.168.1.10 - alice [01/Apr/2026:08:17:30 +0000] "PUT /api/orders/7 HTTP/1.1" 500 0
EOF

La commande s est le cœur de sed. Sa syntaxe :

s/PATTERN/REMPLACEMENT/FLAGS
  • PATTERN : expression régulière (BRE par défaut, ERE avec -E)
  • REMPLACEMENT : le texte de remplacement
  • FLAGS : g (global), i (insensible à la casse), 2 (deuxième occurrence seulement)

Substitution basique, première occurrence par ligne

Section intitulée « Substitution basique, première occurrence par ligne »

La forme minimale remplace prod par staging dans hosts.old et envoie le résultat sur la sortie standard, sans toucher au fichier. Chaque ligne ne contient qu'un prod : la substitution les change donc toutes.

Fenêtre de terminal
sed 's/prod/staging/' hosts.old
192.168.1.10 web01.staging.example.com web01
192.168.1.11 web02.staging.example.com web02
192.168.1.20 db01.staging.example.com db01
10.0.0.5 bastion.staging.example.com bastion

Sans flag, sed remplace seulement la première occurrence sur chaque ligne, puis passe à la ligne suivante. Sur un fichier où le mot revient deux fois par ligne, la seconde occurrence reste intacte.

Le flag g (global) applique la substitution à toutes les occurrences de chaque ligne. La démonstration la plus courte passe par echo, avec trois fois le même mot sur une seule ligne.

Fenêtre de terminal
echo "prod prod prod" | sed 's/prod/staging/g'
staging staging staging

Sans -i, sed envoie le résultat sur la sortie standard, et le fichier original reste intact : c'est un filtre, comme grep. Avec -i (in-place), la modification est écrite directement dans le fichier. Le bon réflexe : lancer la commande sans -i, lire la sortie, puis relancer avec -i.

Fenêtre de terminal
cp hosts.old hosts.staging
sed -i 's/prod/staging/g' hosts.staging

L'extension passée juste après -i devient le suffixe du fichier de sauvegarde. Sur GNU sed, elle se colle à l'option (-i.bak), sans espace :

Fenêtre de terminal
sed -i.bak 's/prod/staging/g' hosts.staging
Fenêtre de terminal
ls -1
# hosts.staging ← fichier modifié
# hosts.staging.bak ← copie de l'original

Par défaut, sed applique la commande à toutes les lignes. Une adresse limite l'action à une sélection.

Une adresse numérique désigne une ligne par son numéro, une plage par deux numéros séparés d'une virgule, et la dernière ligne par $. Ce $ n'a rien à voir avec l'ancre de fin de ligne des regex : c'est sa position, avant la commande, qui lui donne ce sens.

Fenêtre de terminal
# Modifier seulement la ligne 2
sed '2s/example/EXAMPLE/' hosts.old
# Modifier les lignes 1 à 2
sed '1,2s/example/EXAMPLE/' hosts.old
# Modifier la dernière ligne
sed '$s/example/EXAMPLE/' hosts.old

La dernière commande ne change que la ligne du bastion :

192.168.1.10 web01.prod.example.com web01
192.168.1.11 web02.prod.example.com web02
192.168.1.20 db01.prod.example.com db01
10.0.0.5 bastion.prod.EXAMPLE.com bastion

Une adresse regex, écrite entre barres obliques, sélectionne les lignes qui contiennent le motif. C'est la forme la plus utile en administration : on cible un bloc de serveurs par son nom plutôt que par un numéro de ligne qui change au moindre ajout.

Fenêtre de terminal
# Modifier seulement les lignes qui contiennent "web"
sed '/web/s/prod/staging/' hosts.old
192.168.1.10 web01.staging.example.com web01
192.168.1.11 web02.staging.example.com web02
192.168.1.20 db01.prod.example.com db01
10.0.0.5 bastion.prod.example.com bastion

Les lignes db01 et bastion ne correspondent pas au motif web : elles sortent inchangées, avec leur prod. Sans adresse, la même substitution aurait tout modifié.

La commande d (delete) supprime de la sortie les lignes sélectionnées par l'adresse. L'ancre ^ compte : sans elle, /#/d supprimerait aussi une ligne valide suivie d'un commentaire en fin de ligne.

Fenêtre de terminal
# Supprimer les commentaires et les lignes vides d'un fichier de config
sed '/^#/d; /^$/d' db.conf
host = db.internal.example.com
port = 5432
user=admin
password= secret123
database=production
max_connections = 100
connect_timeout= 30

Les commentaires (# ...) ont disparu, et une éventuelle ligne vide aurait subi le même sort. Le fichier original est intact : ajoutez -i pour écrire le résultat.

Plutôt que d'enchaîner sed | sed | sed, passez plusieurs expressions avec -e : elles s'appliquent dans l'ordre, en une seule passe sur le fichier. Le point-virgule à l'intérieur d'une même expression ('/^#/d; /^$/d') reste l'équivalent pour les cas courts.

Fenêtre de terminal
sed -E \
-e '/^#/d' \
-e '/^$/d' \
-e 's/[[:space:]]*=[[:space:]]*/=/' \
db.conf
host=db.internal.example.com
port=5432
user=admin
password=secret123
database=production
max_connections=100
connect_timeout=30

Trois transformations appliquées en une passe :

  1. Suppression des commentaires
  2. Suppression des lignes vides
  3. Normalisation des espaces autour de = : [[:space:]]* désigne zéro espace ou plus, le quantificateur * étant détaillé dans l'encadré qui suit

Avec -E, les parenthèses (...) créent des groupes de capture, numérotés de gauche à droite. Les backreférences \1, \2 réinjectent le contenu capturé dans le remplacement, ce qui permet de réordonner des champs sans passer par awk.

Fenêtre de terminal
# Inverser IP et hostname dans hosts.old
sed -E 's/^([0-9.]+)([[:space:]]+)([a-z0-9.]+)/\3\2\1/' hosts.old
web01.prod.example.com 192.168.1.10 web01
web02.prod.example.com 192.168.1.11 web02
db01.prod.example.com 192.168.1.20 db01
bastion.prod.example.com 10.0.0.5 bastion
  • ([0-9.]+) → groupe 1 : un ou plusieurs chiffres ou points, donc l'IP
  • ([[:space:]]+) → groupe 2 : un ou plusieurs espaces ou tabulations, donc la séparation
  • ([a-z0-9.]+) → groupe 3 : un ou plusieurs caractères parmi minuscules, chiffres et points, donc le hostname

Sans le +, chaque classe ne prend qu'un seul caractère : ^[0-9.] s'arrête au 1 de 192.168.1.10, le caractère suivant n'est pas un espace, le motif ne correspond plus et sed laisse la ligne intacte, sans signaler d'erreur.

Dans le remplacement, \3\2\1 réinjecte hostname, séparation, IP, dans le nouvel ordre. La séparation capturée garde sa largeur d'origine, d'où l'alignement irrégulier de la sortie.

Workflow complet, patcher un fichier de config pour une migration

Section intitulée « Workflow complet, patcher un fichier de config pour une migration »

Voici le workflow complet d'une migration de production vers staging : une copie de l'original, une seule passe de sed avec backup, puis deux vérifications. Chaque étape protège contre un type d'erreur différent.

Fenêtre de terminal
# 1. Faire une copie de sécurité
cp db.conf db.conf.orig
# 2. Appliquer toutes les transformations en une seule passe
sed -E -i.bak \
-e 's/host[[:space:]]*=[[:space:]]*.*/host=db.staging.example.com/' \
-e 's/database[[:space:]]*=[[:space:]]*.*/database=staging/' \
-e 's/[[:space:]]*=[[:space:]]*/=/' \
-e '/^#/d' \
-e '/^$/d' \
db.conf
# 3. Vérifier le résultat
cat db.conf
# 4. Vérifier la différence avec l'original
diff db.conf.orig db.conf

Résultat de cat db.conf :

host=db.staging.example.com
port=5432
user=admin
password=secret123
database=staging
max_connections=100
connect_timeout=30

Résultat du diff :

1,3c1,2
< # Configuration base de données
< host = db.internal.example.com
< port = 5432
---
> host=db.staging.example.com
> port=5432
5,9c4,7
< password= secret123
< database=production
< max_connections = 100
< # timeout en secondes
< connect_timeout= 30
---
> password=secret123
> database=staging
> max_connections=100
> connect_timeout=30

Le diff après sed -i est votre vérification obligatoire avant de livrer le fichier : il montre exactement ce qui a changé, et révèle aussitôt une substitution trop large ou un motif qui n'a rien trouvé.

Par défaut, sed affiche toutes les lignes, même non modifiées. L'option -n supprime cet affichage automatique, et la commande p affiche explicitement les lignes sélectionnées. Combinées, elles reproduisent un grep. Oubliez -n, et les lignes qui correspondent sortent en double : sur cet extrait de 3 lignes, la commande en affiche 4.

Fenêtre de terminal
# Afficher seulement les lignes avec code 500 dans access.log
sed -n '/" 500 /p' access.log
192.168.1.10 - alice [01/Apr/2026:08:17:30 +0000] "PUT /api/orders/7 HTTP/1.1" 500 0

Ces pièges ont un point commun : sed ne signale presque jamais d'erreur. Une substitution qui ne trouve rien, une regex écrite en ERE lancée en BRE, un point qui matche trop large : la commande réussit, et seul le résultat trahit le problème. D'où l'importance du diff final.

PiègeSymptômeSolution
Oublier /gSeule la première occurrence est remplacéeAjouter /g au flag
sed -i sans backupFichier original perdu en cas d'erreurUtiliser -i.bak systématiquement
Confondre BRE et ERE+, ? non reconnus sans backslashAjouter -E pour ERE
Slash / dans le patternunknown option to `s'Utiliser un autre délimiteur : s|/old/path|/new/path|g
Point . non échappéMatche trop largeÉchapper : \. pour un point littéral
Modifier le fichier livré par le paquetPerdu à la prochaine mise à jour du paquetÉcrire la modification dans un fichier de surcharge (/etc/<service>/conf.d/, ou systemctl edit pour une unité systemd)
  • s/avant/après/g : substitution globale sur chaque ligne
  • -i.bak : modification in-place avec backup, toujours en production
  • Adresse numérique 2s/... ou regex /pattern/s/... pour cibler des lignes
  • /pattern/d : supprimer les lignes qui correspondent
  • -e : enchaîner plusieurs commandes en une passe
  • Quantificateurs : * zéro fois ou plus, + une fois ou plus, ? facultatif, {n,m} entre n et m fois
  • -E : activer ERE pour +, ?, {n,m}, groupes de capture (...)
  • \1, \2 : réinjecter un groupe capturé dans le remplacement
  • -n ... p : afficher seulement les lignes sélectionnées (comme grep)
  • Vérifier avec diff après tout sed -i sur un fichier de config

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn