DevGuide

L'antisèche grep : chercher dans un fichier, un arbre

Sur cette page
  1. Chercher dans un seul fichier
  2. Chercher tout un arbre (la commande de tous les jours)
  3. Ignorer la casse, parce que les logs ne sont jamais d'accord
  4. Montrer des lignes, en cacher : -v, -o, le contexte
  5. Ne chercher que certains fichiers dans un arbre
  6. Les motifs : quand mettre -E, et quand mettre -F
  7. Et après
  8. Sources

L'antisèche grep que vous cherchez met les quatre commandes que vous tapez vraiment tout en haut, pour que vous en copiiez une, changiez votre motif, et filiez. grep prend un motif et un fichier, affiche les lignes qui correspondent, et la ferme sur le reste. Il fait neuf choses qui vous servent et une bonne centaine que vous ne toucherez jamais, donc cette page saute la centaine. Chercher dans un seul fichier, parcourir toute une arborescence avec grep -rn, ignorer la casse avec -i, afficher les numéros de ligne avec -n, inverser la correspondance avec -v, et choisir votre moteur avec -E pour la regex étendue ou -F pour une chaîne littérale fixe.

The short answer

Chercher dans un fichier : grep "error" app.log (le motif d'abord, le fichier ensuite). Chercher tout un arbre : grep -rn "TODO" ., le r passe en récursif, le n ajoute les numéros de ligne, et c'est la combinaison où vous allez vivre. Ajoutez -i pour ignorer la casse, -v pour inverser la correspondance, et -F quand votre motif est du texte littéral avec des points ou des slashs que vous voulez laisser tranquilles.

grep -rnla commande de tous les jours
~9 optionscouvrent presque tout
-E / -Fregex ou littéral
Carte réponse : chercher dans un fichier avec grep motif fichier, chercher un arbre avec grep -rn, ignorer la casse avec -i, inverser avec -v, et choisir la regex étendue -E ou la chaîne fixe -F.
La poignée de recettes grep que vous dégainez vraiment, rangées par ce que vous cherchez. PNG

Vous avez un mot, et vous voulez savoir où il se cache. Dans un fichier, ou perdu quelque part dans une arborescence de deux cents. C'est le boulot, grep est l'outil, donc les quatre trucs que vous allez vraiment taper sont tout en haut. Vous copiez, vous changez le motif, vous filez. Le truc avec grep, c'est qu'il fait neuf choses qui vous servent et une bonne centaine que vous ne toucherez jamais. Cette page saute la centaine.

Une mise au point avant les recettes. grep prend un motif et un fichier, affiche les lignes qui correspondent, et la ferme sur le reste. Par défaut, le motif est une expression régulière, ce qui pose problème le jour où vous cherchez un truc avec un point ou un crochet dedans et que le résultat part en vrille. Il y a une option pour ça, j'y reviens.

Chercher dans un seul fichier

La version de base, et la plupart des gens commencent ici. grep "motif" fichier. Vous mettez le motif entre guillemets pour que le shell n'aille pas dilater un truc bizarre dedans, puis vous nommez le fichier. grep le lit de haut en bas et crache chaque ligne qui contient une correspondance. Voilà tout le contrat.

CommandeCe qu'elle fait
grep "error" app.logAffiche chaque ligne d'app.log contenant error
grep "error" app.log access.logCherche dans plusieurs fichiers à la fois, chaque match préfixé de son nom de fichier
grep -n "error" app.logPareil, mais avec le numéro de ligne devant chaque trouvaille
grep -c "error" app.logCompte les lignes qui matchent au lieu de les afficher

Mettez toujours le motif entre guillemets. Même quand c'est un seul mot et que vous croyez ne pas en avoir besoin. Le jour où vous oubliez, c'est le jour où votre motif a une espace ou un caractère spécial dedans, et le shell en bouffe la moitié avant que grep ne voie quoi que ce soit. L'habitude bat la finesse, ici.

Chercher tout un arbre (la commande de tous les jours)

Voici celle que je tape plus que n'importe quelle autre, et de loin. grep -rn "truc" . parcourt le dossier courant et tout ce qu'il y a dessous, affiche chaque match avec son chemin et son numéro de ligne. Le r c'est récursif, le n les numéros de ligne, le . veut dire pars d'ici. Franchement, grep -rn c'est LA commande. La plupart du temps, le reste c'est de la garniture.

CommandeCe qu'elle fait
grep -r "api_key" .Récursivement, cherche dans chaque fichier sous le dossier courant
grep -rn "api_key" .Pareil, plus le chemin et le numéro de ligne sur chaque trouvaille
grep -rn "api_key" src/Limite le parcours à un sous-dossier au lieu de tout
grep -rl "api_key" .Juste les noms de fichiers qui le contiennent, un par ligne, sans le texte

La dernière, -rl, est discrètement excellente. Parfois vous vous fichez de ce que dit la ligne, vous voulez juste la liste des fichiers qui mentionnent un truc pour aller les ouvrir. Vous renommez une fonction dans tout un dépôt ? grep -rl vous tend l'ensemble exact des fichiers à toucher, et rien d'autre.

Ignorer la casse, parce que les logs ne sont jamais d'accord

Les logs et les fichiers de conf n'arrivent pas à décider si c'est Error, ERROR ou error, et vous ne devriez pas avoir à vous en soucier. -i rend la correspondance insensible à la casse. Une lettre, et votre recherche arrête de rater des trucs juste parce que quelqu'un a appuyé sur Maj.

CommandeCe qu'elle fait
grep -i "warning" fileMatche warning, Warning, WARNING, peu importe la casse
grep -rin "timeout" .Récursif, insensible à la casse, avec numéros de ligne. Une ligne de cheval de trait
grep -iw "get" routes.rbInsensible à la casse, mot entier, donc get matche mais pas target

Le -w au bout, là, c'est le partenaire oublié de -i. Sans lui, chercher get traîne target, forget, widget, tout le paquet. -w dit "matche ça comme un mot à part entière, avec des frontières des deux côtés", et le bruit s'efface. J'oublie qu'il existe une fois sur deux, et je m'en souviens pile au moment où mon écran se remplit de cochonneries.

Terminal montrant grep -rn avec les numéros de ligne parcourant un arbre source, puis grep -i ajoutant la correspondance insensible à la casse sur un fichier de log.
grep -rn est la ligne où vous allez vivre : récursive, avec le chemin et le numéro de ligne sur chaque trouvaille. PNG

Montrer des lignes, en cacher : -v, -o, le contexte

Après les bases, quelques options remodèlent ce que grep vous rend. Inverser la correspondance pour voir ce qui ne matche pas. Sortir uniquement la partie qui colle au lieu de la ligne entière. Attraper les lignes autour d'un match pour avoir le contexte. Ce sont celles qui transforment grep d'un chercheur en quelque chose de plus proche d'un scalpel.

CommandeCe qu'elle fait
grep -v "debug" app.logInverse : affiche les lignes qui NE contiennent PAS debug
grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" access.logAffiche seulement le match (ici, les adresses IP), pas la ligne entière
grep -A3 "panic" app.logLe match plus les 3 lignes après (A pour after)
grep -B3 "panic" app.logLe match plus les 3 lignes avant (B pour before)
grep -C3 "panic" app.log3 lignes autour du match, des deux côtés (C pour context)

Les options de contexte gagnent leur croûte quand vous lisez une stack trace ou un log. Un match tout nu sur panic vous montre le panic et rien de ce qui l'a amené, ce qui est inutile. -C3 tire les trois lignes de chaque côté pour que vous voyiez vraiment ce qui s'est passé. Je pars sur -C3 par défaut et je monte le chiffre quand trois ne suffit pas. Parfois c'est cinq, parfois vingt, ça dépend du bavardage du log.

Ne chercher que certains fichiers dans un arbre

Un simple grep -r lit tout, y compris le marécage node_modules et ce bundle minifié de 80 Mo que vous aviez oublié. Le glob --include clôture la recherche récursive aux types de fichiers que vous visez, ce qui est plus rapide et bien moins bruyant.

CommandeCe qu'elle fait
grep -rn --include='*.js' "fetch" .Recherche récursive, mais seulement dans les .js
grep -rn --include='*.py' "import requests" .Seulement les fichiers Python sous ce dossier
grep -rn --include='*.{c,h}' "malloc" .Deux extensions à la fois avec un glob à accolades
grep -rn --exclude-dir=node_modules "TODO" .Saute tout un dossier que vous ne voulez jamais lire

Mettez le glob entre guillemets ('*.js', guillemets compris) pour que le shell passe le motif littéral à grep au lieu de le dilater contre votre dossier courant d'abord. Ce détail mord. --exclude-dir=node_modules est l'autre moitié de la santé mentale dans un projet JS, parce que personne n'a jamais voulu grepper dans node_modules volontairement.

Mon avis : grep -rn est la commande de tous les jours, et ripgrep vaut un coup d'oeil

Si vous n'apprenez qu'un seul grep, apprenez grep -rn "motif" . et ajoutez -i quand la casse mord. Cette seule ligne répond à "où est ce truc dans mon code" neuf fois sur dix, et elle est sur chaque machine Linux, chaque Mac, chaque conteneur, chaque serveur verrouillé où vous mettrez les pieds en SSH. Cette omniprésence, c'est tout l'intérêt. Maintenant la partie honnête : il y a un outil plus récent, ripgrep (la commande c'est rg), qui est vraiment plus rapide sur les gros arbres et qui saute le bazar listé dans .gitignore tout seul, sans aucune option. Sur un gros dépôt la différence est réelle, on la sent. Mais c'est une installation à part, et il n'est tout simplement pas là sur un serveur neuf. Du coup je dégaine rg sur ma propre machine et je retombe sur grep -rn à l'instant où je suis quelque part d'inconnu. C'est peut-être moi qui suis flemmard à installer des trucs, je ne sais pas. Connaître grep sur le bout des doigts ne m'a jamais laissé tomber une seule fois, et je ne peux pas en dire autant d'un truc que j'ai dû apt install d'abord.

Les motifs : quand mettre -E, et quand mettre -F

grep parle expressions régulières, et c'est un cadeau autant qu'un piège. Un cadeau quand vous voulez matcher un motif comme une IP ou une date. Un piège quand vous voulez juste trouver une chaîne littérale qui se trouve contenir un point ou un crochet, et que grep les lit comme des métacaractères de regex et vous rend n'importe quoi. Deux options règlent ça.

CommandeCe qu'elle fait
grep -E "[0-9]{3}-[0-9]{4}" fileRegex étendue : {3} et compagnie marchent sans backslash d'échappement
grep -E "error|warning|fatal" app.logMatche plusieurs motifs d'un coup avec l'alternative |
grep -F "192.168.1.1" hostsChaîne fixe : les points sont des points littéraux, sans magie regex
grep -Fw "v1.2.0" CHANGELOG.mdChaîne littérale, mot entier, pour chercher des numéros de version sans risque

La règle rapide que j'utilise vraiment : si votre motif contient {}, |, + ou () et que vous voulez qu'ils veuillent dire quelque chose, prenez -E. Si votre motif est une chaîne littérale avec des points ou des slashs que vous voulez laisser tranquilles, prenez -F. Chercher une adresse IP avec un grep ordinaire sans -F marche techniquement, parce qu'un point matche n'importe quel caractère, mais ça matchera aussi des trucs non voulus, genre 192x168x1x1 dans une ligne de log. -F tue cette ambiguïté net.

Et après

Voilà l'ensemble qui sert. Chercher dans un fichier, dans un arbre avec -rn, ignorer la casse avec -i, inverser avec -v, sortir juste le match avec -o, compter avec -c, lister les fichiers avec -l, mot entier avec -w, le contexte avec -A -B -C, clôturer les types de fichiers avec --include, et choisir votre moteur de motif avec -E ou -F. Ça couvre vraiment ce que je fais avec grep dans une semaine normale, et l'option exotique de temps en temps que je vais chercher comme tout le monde.

Tant que vous vivez dans le shell, la même habitude (copier plutôt que mémoriser) paie à côté. Vous traquez des fichiers par nom ou par taille plutôt que par contenu ? L'antisèche de la commande find range ces recettes pareil. Vous construisez la regex elle-même et vous voulez la voir matcher en direct avant de lui faire confiance ? Un testeur regex montre les correspondances pendant que vous tapez. Vous fouillez les connexions et les interfaces ? Voyez les commandes réseau Linux avec ip et ss. Et si vous sautez d'une distrib à l'autre sans vous rappeler si c'est apt ou dnf aujourd'hui, une référence des commandes par distribution Linux vous déballe tout.

Sources

Questions fréquentes

Comment chercher un mot dans un fichier avec grep ?

Lancez grep "mot" fichier. Le motif vient d'abord, puis le fichier, et grep affiche chaque ligne qui contient une correspondance. Mettez le motif entre guillemets même pour un seul mot, pour que le shell ne l'abîme pas s'il a une espace ou un caractère spécial. Ajoutez -n si vous voulez aussi le numéro de ligne devant chaque trouvaille.

Comment grepper récursivement dans tout un dossier ?

Utilisez grep -rn "motif" . où le point veut dire le dossier courant. Le r parcourt chaque fichier dessous et le n ajoute les numéros de ligne, donc chaque trouvaille montre son chemin et sa ligne. Pour limiter le bruit dans un projet de code, ajoutez --include='*.js' pour un seul type de fichier ou --exclude-dir=node_modules pour sauter un dossier que vous ne voulez jamais lire.

Comment rendre grep insensible à la casse ?

Ajoutez -i. Alors grep -i "warning" file matche warning, Warning et WARNING pareil. Ça s'empile avec le reste, donc grep -rin "timeout" . est une recherche récursive, insensible à la casse, avec numéros de ligne. Associez-le à -w quand vous voulez un mot entier, pour que get matche mais pas target ni forget.

À quoi sert grep -v ?

Il inverse la correspondance. grep -v "debug" app.log affiche chaque ligne qui NE contient PAS debug, c'est comme ça qu'on filtre le bruit hors d'un log. Empilez-le pour enchaîner les filtres, par exemple grep -v "debug" app.log redirigé vers un grep -v "info", et il ne reste que les lignes qui ne mentionnent ni l'un ni l'autre. Pratique pour réduire un log chargé aux lignes qui comptent vraiment.

Comment chercher une chaîne littérale avec grep, pas une regex ?

Utilisez -F, qui dit à grep de traiter le motif comme du texte fixe au lieu d'une expression régulière. Ainsi grep -F "192.168.1.1" hosts matche cette chaîne exacte, avec les points lus comme des points littéraux plutôt que comme le joker regex qui matche n'importe quel caractère. Prenez -F dès que votre terme de recherche contient des points, des slashs, des crochets ou d'autres caractères que vous voulez laisser tranquilles.