Guide

grep, awk, sed le trio que vous utilisez mal depuis le début

grep filtre, sed transforme, awk agrège. Guide pratique pour les DevOps : logs nginx, fichiers de config, pipelines et pièges classiques.

Sommaire

En 1973, Ken Thompson a écrit grep en une nuit parce qu'un collègue avait besoin de chercher des patterns dans des fichiers et que l'alternative était d'écrire un programme complet. grep vient de la commande ed - g/re/p - global, regular expression, print. Une commande d'éditeur transformée en outil autonome en une nuit. C'est ainsi que naissent les meilleurs outils Unix : par flemme et par nécessité, dans cet ordre.

sed est arrivé en 1974. awk en 1977 - son nom vient des initiales de ses trois créateurs, Aho, Weinberger et Kernighan, ce qui en dit long sur l'époque où les outils portaient le nom de leurs auteurs plutôt qu'un acronyme à consonance de startup.

Cinquante ans plus tard, ces trois outils sont sur absolument tous les systèmes Unix. Vous les utilisez tous les jours. Et la plupart du temps, vous utilisez grep quand vous devriez utiliser awk, sed quand vous devriez utiliser grep, et awk quand vous devriez utiliser Python mais que vous ne voulez pas l'admettre.

Ce guide remet les pendules à l'heure.

Qui fait quoi - la confusion fondamentale

La raison pour laquelle tout le monde les mélange : leurs périmètres se chevauchent. grep peut faire ce que sed fait. awk peut faire ce que grep et sed font. sed peut faire ce qu'awk fait en version dégradée. C'est 50 ans de compatibilité ascendante et de décisions de design qui semblaient raisonnables à l'époque.

OutilForceFaiblesse
grepChercher, filtrer, extraire des lignesTransformer, reformater
sedTransformer, substituer, supprimer des lignesCalculs, agrégations
awkTout - traitement de colonnes, calculs, logiqueLa lisibilité au-delà de 3 lignes

La règle simple :

  • Vous cherchez quelque chose - grep
  • Vous transformez ligne par ligne - sed
  • Vous traitez des colonnes, calculez, agrégez - awk
  • Votre pipe fait plus de 80 caractères - Python

Retenez ça et vous éviterez 80% des erreurs de choix d'outil.

grep - chercher proprement

Au-delà de grep "mot" fichier

grep -r "pattern" /var/log/    # récursif dans un dossier
grep -l "pattern" /var/log/    # noms de fichiers seulement (pas le contenu)
grep -n "pattern" fichier      # avec numéros de ligne
grep -c "pattern" fichier      # compter les occurrences
grep -v "pattern" fichier      # inverser - lignes qui NE matchent PAS
grep -i "pattern" fichier      # insensible à la casse
grep -w "mot" fichier          # mot entier seulement (pas "motivation" pour "mot")
grep -x "ligne entière" fichier # ligne entière seulement

-o - extraire seulement le match

# Sans -o : affiche la ligne entière
grep "ERROR" app.log
# 2026-05-13 08:42:17 ERROR Connection timeout on host 10.0.1.42

# Avec -o : affiche seulement le match
grep -o "ERROR.*" app.log
# ERROR Connection timeout on host 10.0.1.42

# Extraire les IPs d'un fichier de logs
grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" access.log | sort | uniq -c | sort -rn

-A, -B, -C - le contexte autour du match

grep -A 3 "ERROR" app.log   # 3 lignes après le match
grep -B 3 "ERROR" app.log   # 3 lignes avant le match
grep -C 3 "ERROR" app.log   # 3 lignes avant et après - le plus utile

C'est ce que vous utilisez quand vous cherchez "pourquoi cette erreur est apparue" et que vous avez besoin du contexte. Indispensable pour le débogage de logs.

-E vs -P - les regex étendues vs PCRE

# Regex basiques (défaut) - limitées, archaïques
grep "foo\|bar" fichier        # OU en regex basique - peu intuitif

# Regex étendues ERE - plus lisibles
grep -E "foo|bar" fichier      # OU
grep -E "https?://" fichier    # http ou https
grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}" fichier  # date ISO au début de ligne

# PCRE - Perl Compatible Regular Expressions - le plus puissant
grep -P "\d{4}-\d{2}-\d{2}" fichier     # \d, \w, \s, lookahead, lookbehind
grep -P "(?<=Bearer )\S+" fichier        # lookbehind : extraire le token après "Bearer "
grep -P "\b\w+(?=\.log)\b" fichier       # lookahead : mot avant ".log"

-E pour les cas courants. -P quand vous avez besoin de lookahead, lookbehind, ou des raccourcis \d, \w, \s. Sur macOS, grep -P n'est pas disponible nativement - installez grep via Homebrew (brew install grep) et utilisez ggrep.

zgrep et les fichiers compressés

# Sans zgrep
gunzip -c access.log.gz | grep "ERROR"

# Avec zgrep
zgrep "ERROR" access.log.gz
zgrep "ERROR" /var/log/nginx/*.gz

Vos logs sont rotatés et compressés. zgrep vous évite le gunzip intermédiaire. Ça paraît trivial, ça fait gagner du temps tous les jours.

Combiner avec find

# Chercher dans les fichiers modifiés dans les 24 dernières heures
find /var/log -name "*.log" -mtime -1 | xargs grep "ERROR"

# Chercher en excluant les dossiers
grep -r --exclude-dir={node_modules,.git,vendor} "pattern" .

# Chercher uniquement dans certains types de fichiers
grep -r --include="*.conf" "pattern" /etc/

Les pièges des guillemets

# Guillemets simples - le pattern est littéral
grep 'pattern avec $variable' fichier    # cherche littéralement $variable

# Guillemets doubles - les variables sont interpolées
grep "pattern avec $variable" fichier    # cherche la valeur de $variable
grep "pattern avec \$variable" fichier   # cherche littéralement $variable

# Règle simple : guillemets simples sauf si vous avez besoin d'interpolation

sed - transformer des flux

sed (stream editor) traite un fichier ligne par ligne et applique des transformations. La commande la plus connue est s/foo/bar/ - substituer. C'est 10% de ce que sed peut faire.

La substitution - au-delà de s/foo/bar/g

sed 's/foo/bar/' fichier        # première occurrence par ligne
sed 's/foo/bar/g' fichier       # toutes les occurrences (g = global)
sed 's/foo/bar/2' fichier       # deuxième occurrence seulement
sed 's/foo/bar/gi' fichier      # toutes, insensible à la casse

# Substituer dans des lignes contenant un pattern
sed '/ERROR/s/timeout/TIMEOUT/g' fichier

# Délimiteur alternatif - utile pour les URLs
sed 's|http://|https://|g' fichier
sed 's,/old/path,/new/path,g' fichier

Les adresses - cibler des lignes spécifiques

sed '5s/foo/bar/' fichier           # seulement ligne 5
sed '5,10s/foo/bar/' fichier        # lignes 5 à 10
sed '5,$s/foo/bar/' fichier         # ligne 5 jusqu'à la fin

sed '/ERROR/s/foo/bar/' fichier     # lignes contenant ERROR
sed '/START/,/END/s/foo/bar/' fichier   # entre START et END inclus

sed '/ERROR/!d' fichier             # supprimer les lignes qui NE contiennent PAS ERROR

Supprimer des lignes

sed '/^$/d' fichier                          # supprimer les lignes vides
sed '/^[[:space:]]*$/d' fichier              # vides ou avec seulement des espaces
sed '/^#/d' fichier                          # supprimer les commentaires
sed '/^[[:space:]]*#/d' fichier              # commentaires avec indentation
sed '/ERROR/d' fichier                       # lignes contenant un pattern
sed '1,5d' fichier                           # les N premières lignes
sed '$d' fichier                             # la dernière ligne
sed '$!N; /^\(.*\)\n\1$/!P; D' fichier      # doublons consécutifs

Ajouter des lignes

sed '/PATTERN/a\Nouvelle ligne' fichier  # après un pattern
sed '/PATTERN/i\Nouvelle ligne' fichier  # avant un pattern
sed '5a\Nouvelle ligne' fichier          # après la ligne N

Extraire des blocs entre deux patterns

sed -n '/START/,/END/p' fichier                          # inclus
sed -n '/START/,/END/{/START/d;/END/d;p}' fichier        # exclus

# Extraire un server block nginx
sed -n '/server {/,/^}/p' /etc/nginx/nginx.conf

Édition en place - modifier le fichier directement

# GNU sed (Linux)
sed -i 's/foo/bar/g' fichier

# BSD sed (macOS) - nécessite une extension (même vide)
sed -i '' 's/foo/bar/g' fichier
sed -i.bak 's/foo/bar/g' fichier  # crée fichier.bak avant modification

La différence BSD vs GNU - le piège macOS/Linux

C'est l'erreur classique du script qui fonctionne sur Linux et casse sur macOS (ou l'inverse). Les différences principales :

# Édition en place
# GNU : sed -i 's/foo/bar/' fichier
# BSD : sed -i '' 's/foo/bar/' fichier

# Regex étendues
# GNU : sed -E ou sed -r
# BSD : sed -E uniquement

# \w, \d, \s
# GNU : supportés
# BSD : non supportés - utilisez [[:alnum:]], [0-9], [[:space:]]

Si vos scripts doivent tourner sur les deux : soit vous installez GNU sed sur macOS (brew install gnu-sed, puis gsed), soit vous écrivez des regex POSIX sans \w et \d. La deuxième option est plus portable, la première est plus rapide à écrire.

Les backreferences - capturer et réutiliser

# Inverser deux champs séparés par ":"
sed -E 's/([^:]*):([^:]*)/\2:\1/' fichier
# "foo:bar" → "bar:foo"

# Encadrer chaque mot de guillemets
sed -E 's/([^ ]+)/"\1"/g' fichier

# Extraire le domaine d'une URL
sed -E 's|https?://([^/]+).*|\1|' fichier

awk - le vrai couteau suisse

awk est mal compris parce qu'il est présenté comme un outil alors que c'est un langage de programmation. Un langage minimaliste, certes, mais un langage - avec des variables, des conditions, des boucles, des tableaux associatifs, des fonctions. Ça explique pourquoi les one-liners awk ont l'air cryptiques : vous lisez du code, pas une commande.

La structure de base

awk 'pattern { action }' fichier

Pour chaque ligne du fichier, si pattern est vrai, exécuter action. Si le pattern est absent, exécuter action sur toutes les lignes. Si l'action est absente, afficher la ligne.

awk '{ print }' fichier          # afficher toutes les lignes (équivalent cat)
awk '1' fichier                  # 1 est toujours vrai

awk '/ERROR/' fichier            # lignes contenant ERROR (équivalent grep)
awk '!/ERROR/' fichier           # lignes ne contenant pas ERROR (équivalent grep -v)

Les champs - la vraie force d'awk

awk découpe chaque ligne en champs séparés par un délimiteur (espace par défaut). $1 est le premier champ, $2 le deuxième, $NF le dernier, $0 la ligne entière.

awk '{ print $1 }' fichier          # premier champ
awk '{ print $1, $3 }' fichier      # premier et troisième champ
awk '{ print $NF }' fichier         # dernier champ
awk '{ print $(NF-1) }' fichier     # avant-dernier champ
awk '{ print $2, $1 }' fichier      # inverser deux colonnes

FS et OFS - changer les séparateurs

awk -F: '{ print $1, $3 }' /etc/passwd     # login et UID
awk -F: '{ print $1 }' /etc/passwd          # logins seulement
awk -F, '{ print $2 }' data.csv             # CSV
awk -F'[,;:]' '{ print $1 }' fichier        # regex comme séparateur

# Changer le séparateur de sortie
awk -F: 'OFS=";" { print $1, $3, $6 }' /etc/passwd
# login;uid;home

BEGIN et END - avant et après le traitement

# Header
awk 'BEGIN { print "LOGIN\tUID\tHOME" } -F: { print $1"\t"$3"\t"$6 }' /etc/passwd

# Compter les lignes
awk 'END { print NR, "lignes" }' fichier

# Sommer une colonne
awk '{ sum += $2 } END { print "Total:", sum }' fichier

# Moyenne
awk '{ sum += $2; count++ } END { print "Moyenne:", sum/count }' fichier

NR = numéro de la ligne courante. NF = nombre de champs dans la ligne courante. Ce sont des variables automatiques d'awk que vous utiliserez constamment.

Conditions et filtres

awk '$3 > 1000' fichier                        # champ 3 supérieur à 1000
awk '$1 == "ERROR"' fichier                    # champ 1 égal à "ERROR"
awk '$1 == "ERROR" && $3 > 500' fichier        # ET
awk '$1 == "ERROR" || $1 == "WARN"' fichier    # OU
awk '$1 ~ /ERROR|WARN/' fichier                # champ 1 matche la regex
awk '$1 !~ /DEBUG/' fichier                    # champ 1 ne matche pas

Les tableaux associatifs - compter et agréger

# Compter les occurrences d'une valeur
awk '{ count[$1]++ } END { for (k in count) print count[k], k }' fichier | sort -rn

# Compter les codes HTTP dans les logs nginx
awk '{ count[$9]++ } END { for (code in count) print code, count[code] }' access.log | sort

# Sommer par catégorie
awk '{ sum[$1] += $2 } END { for (k in sum) print k, sum[k] }' fichier

# Top 10 des IPs dans les logs nginx
awk '{ count[$1]++ } END { for (ip in count) print count[ip], ip }' access.log | sort -rn | head -10

Reformater des sorties

# printf pour le formatage précis
awk '{ printf "%-20s %10.2f\n", $1, $2 }' fichier
# "nom                 1234.56"

# Reformater /etc/passwd en tableau lisible
awk -F: '{ printf "%-15s %-6s %s\n", $1, $3, $6 }' /etc/passwd

# Convertir CSV en INSERT SQL
awk -F, '{ print "INSERT INTO table VALUES (\""$1"\", \""$2"\", "$3");" }' data.csv

Les boucles et la logique

awk 'BEGIN { for (i=1; i<=10; i++) print i }'

awk '{ if ($3 > 1000) print "gros:", $0; else print "petit:", $0 }' fichier

# Fonctions built-in
awk '{ print length($0) }' fichier           # longueur de la ligne
awk '{ print toupper($1) }' fichier          # majuscules
awk '{ print substr($0, 5, 10) }' fichier    # sous-chaîne
awk '{ gsub(/foo/, "bar"); print }' fichier  # substitution globale

Parsing de logs - les cas d'usage concrets

Logs nginx - l'exemple canonique

Format nginx par défaut :

93.184.216.34 - - [13/May/2026:08:42:17 +0200] "GET /api/radar HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
$1           $2$3 $4                            $5                        $6  $7   $8  $9
# Compter les codes HTTP
awk '{ count[$9]++ } END { for (code in count) print code, count[code] }' access.log | sort

# Top 10 des IPs
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -10

# Top 10 des URLs les plus demandées
awk '{ print $7 }' access.log | sort | uniq -c | sort -rn | head -10

# Requêtes avec code 5xx - IP et URL seulement
awk '$9 ~ /^5/ { print $1, $7, $9 }' access.log

# Volume de données transférées par IP
awk '{ bytes[$1] += $10 } END { for (ip in bytes) print bytes[ip], ip }' access.log | sort -rn | head -10

# Requêtes lentes (si le temps de réponse est dans les logs)
awk '$NF > 1.000 { print $NF, $7 }' access.log | sort -rn | head -20

# Filtrer par plage de temps
awk '$4 >= "[13/May/2026:08:00:00" && $4 <= "[13/May/2026:09:00:00"' access.log

Logs applicatifs JSON - grep + awk avant jq

Quand les logs sont du JSON mais que jq n'est pas installé ou que vous voulez une extraction rapide :

# Extraire les messages d'erreur
grep '"level":"error"' app.log | awk -F'"message":"' '{ print $2 }' | cut -d'"' -f1

# Compter les erreurs par type
grep '"level":"error"' app.log | \
  grep -oP '"error_code":"\K[^"]+' | \
  sort | uniq -c | sort -rn

# Filtrer par timestamp
awk -F'"timestamp":"' '$2 >= "2026-05-13T08:00:00"' app.log

Logs syslog

# Erreurs SSH - IPs qui tentent de s'authentifier
grep "Failed password" /var/log/auth.log | awk '{ print $11 }' | sort | uniq -c | sort -rn

# Services qui crashent
grep "segfault" /var/log/syslog | awk '{ print $5 }' | sort | uniq -c | sort -rn

# Activité par heure
awk '{ print substr($3, 1, 2) }' /var/log/syslog | sort | uniq -c

Analyser des logs compressés sur plusieurs jours

# Grep sur tous les logs nginx des 7 derniers jours
zgrep "ERROR" /var/log/nginx/access.log* | awk '{ print $9 }' | sort | uniq -c | sort -rn

# Agrégation cross-fichiers
zcat /var/log/nginx/access.log.*.gz | \
  awk '$9 ~ /^5/ { count[$9]++ } END { for (c in count) print c, count[c] }'

Manipulation de fichiers de config

Remplacer une valeur sans ouvrir vim

sed -i 's/^max_connections = .*/max_connections = 200/' /etc/postgresql/postgresql.conf
sed -i 's/replicas: [0-9]*/replicas: 5/' deployment.yaml
sed -i 's/listen 80/listen 8080/g' /etc/nginx/nginx.conf
sed -i 's|http://old-server.example.com|https://new-server.example.com|g' config.yml

Commenter et décommenter des lignes

sed -i '/max_connections/s/^/#/' postgresql.conf     # commenter
sed -i '/^#max_connections/s/^#//' postgresql.conf   # décommenter

# Commenter un bloc entre deux patterns
sed -i '/^server {/,/^}/s/^/#/' nginx.conf

Ajouter une ligne après un pattern

# Ajouter une directive après "server_name"
sed -i '/server_name/a\    return 301 https://$host$request_uri;' nginx.conf

# Ajouter une ligne si elle n'existe pas déjà
grep -qF "ligne_a_ajouter" fichier || echo "ligne_a_ajouter" >> fichier

Supprimer les doublons en conservant l'ordre

sed '$!N; /^\(.*\)\n\1$/!P; D' fichier  # doublons consécutifs
awk '!seen[$0]++' fichier               # tous les doublons (plus fiable)
awk '!seen[$1]++' fichier               # doublons sur un champ spécifique

Extraire des blocs de config

# Extraire un server block nginx par server_name
awk '/server_name rudeops.com/,/^}/' /etc/nginx/sites-enabled/default

# Extraire une section [mysqld] d'un my.cnf
awk '/^\[mysqld\]/,/^\[/' /etc/mysql/my.cnf | grep -v '^\['

# Extraire les variables d'un fichier .env
grep -v '^#' .env | grep -v '^$' | awk -F= '{ print $1 }'

Valider le format d'un fichier

# Vérifier qu'un fichier CSV a bien N colonnes
awk -F, 'NF != 5 { print "Ligne", NR, ": attendu 5 colonnes, trouvé", NF }' data.csv

# Détecter les lignes trop longues
awk 'length > 120 { print NR": "length" caractères" }' fichier

Pipelines - combiner les trois

C'est ici que ça devient de l'art - ou du n'importe quoi selon votre tolérance aux one-liners illisibles.

Analyse complète des logs nginx en une commande

# Top 10 IPs avec leurs codes HTTP et volumes
awk '{ print $1, $9, $10 }' access.log | \
  awk '{ ip[$1]++; code[$1"-"$2]++; bytes[$1]+=$3 }
       END { for (i in ip) print ip[i], i, bytes[i] }' | \
  sort -rn | head -10

Détecter des patterns d'attaque

# IPs avec plus de 100 erreurs 401 (tentatives d'auth)
awk '$9 == "401" { count[$1]++ }
     END { for (ip in count) if (count[ip] > 100) print count[ip], ip }' access.log | \
  sort -rn

# IPs qui scannent (beaucoup de 404)
awk '$9 == "404" { count[$1]++ }
     END { for (ip in count) if (count[ip] > 50) print count[ip], ip }' access.log | \
  sort -rn

# User-agents suspects
awk '{ print $12 }' access.log | \
  sort | uniq -c | sort -rn | \
  grep -iE "scanner|bot|crawler|nikto|sqlmap|nmap"

Rapport de déploiement depuis les logs

grep "deployment" app.log | \
  sed 's/\(....-..-.. \).*/\1/' | \
  sort | uniq -c | \
  awk '{ print $2, ":", $1, "déploiements" }'

Transformer un CSV en commandes shell

# CSV : server,user,port - générer des commandes SSH
awk -F, '{ printf "ssh -p %s %s@%s\n", $3, $2, $1 }' servers.csv

# Générer des commandes rsync
awk -F, 'NR>1 { printf "rsync -avz /local/path/ %s@%s:/remote/path/\n", $2, $1 }' servers.csv

Monitoring en temps réel

# Taux d'erreur en temps réel
tail -f access.log | awk '
  { total++ }
  $9 ~ /^5/ { errors++ }
  total % 100 == 0 {
    printf "Total: %d | Erreurs 5xx: %d | Taux: %.2f%%\n",
    total, errors, (errors/total)*100
  }
'

Les pièges classiques

Les regex POSIX vs ERE vs PCRE - le chaos organisé

# POSIX BRE (défaut grep et sed) - peu intuitif
grep "fo\+o" fichier             # + doit être échappé
grep "\(foo\)\|\(bar\)" fichier  # groupes et | doivent être échappés

# ERE (-E pour grep et sed) - recommandé
grep -E "fo+o" fichier
grep -E "(foo)|(bar)" fichier

# PCRE (-P pour grep uniquement)
grep -P "\d+" fichier
grep -P "(?<=prefix)\w+" fichier  # lookbehind

La règle simple : utilisez toujours -E pour grep et sed. Évitez les regex BRE - elles sont là pour la compatibilité historique, pas pour vous rendre heureux.

Le piège des espaces dans les noms de fichiers

# Ça casse si les noms de fichiers contiennent des espaces
grep "pattern" $(find /var/log -name "*.log")

# La bonne façon
find /var/log -name "*.log" -exec grep "pattern" {} +
find /var/log -name "*.log" -print0 | xargs -0 grep "pattern"

awk et les grands nombres

# awk utilise des flottants - peut perdre de la précision sur les grands entiers
awk 'BEGIN { OFMT="%.0f" } { print $1 * $2 }' fichier

sed et les caractères spéciaux dans le pattern

# Ces caractères doivent être échappés : . * [ ^ $ \ /
sed 's/http:\/\/old.com/http:\/\/new.com/g' fichier  # échappement
sed 's|http://old.com|http://new.com|g' fichier       # délimiteur alternatif - plus lisible

sed 's/version\.old/version.new/g' fichier  # échapper le point

Quand arrêter et passer à Python

# Votre pipe ressemble à ça ?
cat fichier | grep "pattern" | sed 's/foo/bar/g' | awk -F: '{ print $2 }' | \
  grep -v "^$" | sort | uniq -c | sort -rn | head -10 | \
  awk '{ print $2, ":", $1 }' | sed 's/:/→/'

# C'est le moment de passer à Python
python3 -c "
import re
from collections import Counter

with open('fichier') as f:
    lines = [l for l in f if re.search('pattern', l)]
    values = [l.split(':')[1].strip() for l in lines if l.strip()]
    for val, count in Counter(values).most_common(10):
        print(f'{val} : {count}')
"

La règle empirique : si votre pipe fait plus de deux transformations distinctes, si vous avez besoin de garder un état entre les lignes, ou si vous passez plus de 10 minutes à débugger un one-liner, Python est la bonne réponse. Ce n'est pas une défaite. C'est du pragmatisme.

À retenir

CommandePour quoi
grepChercher, filtrer des lignes
grep -ERegex étendues lisibles
grep -PLookbehind, lookahead, \d, \w
grep -oExtraire seulement le match
grep -C 3Contexte autour du match
sed 's/foo/bar/g'Substitution simple
sed -n '/START/,/END/p'Extraire un bloc
sed '/^$/d'Supprimer les lignes vides
awk '{ print $2 }'Extraire une colonne
awk -F: '{ print $1 }'Changer le séparateur
awk '{ count[$1]++ } END {...}'Compter par catégorie
awk '!seen[$0]++'Supprimer les doublons

FAQ

grep, sed ou awk pour chercher dans des logs ?

grep pour filtrer les lignes. awk pour extraire des champs et agréger. sed pour transformer le format. En pratique, grep filtre d'abord, awk traite ensuite. sed intervient rarement dans les pipelines de logs - il est plus à sa place dans la manipulation de fichiers de config.

Pourquoi mon sed fonctionne sur Linux mais pas sur macOS ?

BSD sed vs GNU sed. Les deux différences les plus courantes : sed -i nécessite '' sur macOS, et \w, \d, \s ne sont pas supportés en BSD sed. Solution : brew install gnu-sed et utilisez gsed, ou écrivez des regex POSIX avec [[:alnum:]], [0-9], [[:space:]].

Quelle différence entre `awk '{ print $0 }'` et `cat` ?

En pratique pour afficher un fichier, aucune. Mais awk traite ligne par ligne avec la possibilité d'appliquer des conditions et transformations, là où cat se contente de copier le flux. awk '1' est l'équivalent de cat mais avec tout le moteur awk derrière.

`cut` vs `awk` pour extraire des colonnes ?

cut est plus simple et plus rapide pour les cas basiques avec un délimiteur fixe : cut -d: -f1 /etc/passwd. awk est plus flexible : vous pouvez combiner des champs, appliquer des conditions, changer le format de sortie. Pour un simple print $1, utilisez cut. Pour tout le reste, awk.

Comment débugger un one-liner awk complexe ?

Ajoutez { print NR, NF, $0 } avant votre action pour voir le numéro de ligne, le nombre de champs, et le contenu de chaque ligne traitée. Ou décomposez en plusieurs passes - grep filtre d'abord, puis awk traite. Un pipe clair de 3 commandes simples vaut mieux qu'un one-liner illisible de 200 caractères, même si c'est moins impressionnant à la machine à café.

Pour aller plus loin

  • The AWK Programming Language - le livre original d'Aho, Weinberger et Kernighan, disponible gratuitement
  • sed & awk - O'Reilly, la référence pratique
  • regex101.com - tester vos regex avec explication pas à pas. Indispensable. Vous l'avez tous déjà dans les favoris.
  • man awk, man sed, man grep - les pages de manuel sont bien écrites pour une fois. Surtout celle d'awk.