Recherche de site Web

10 exemples pratiques de commandes Grep pour les développeurs


La commande grep est utilisée pour rechercher des modèles dans les fichiers. Ce didacticiel présente certains des exemples de commandes grep les plus courants qui seraient particulièrement utiles aux développeurs de logiciels.

Récemment, j'ai commencé à travailler avec Asciidoctor.js et sur les modèles Asciidoctor.js-pug et Asciidoctor. js projet.

Il n’est pas toujours évident d’être immédiatement efficace lorsqu’on fouille pour la première fois dans une base de code contenant plusieurs milliers de lignes. Mais mon arme secrète pour me frayer un chemin à travers autant de lignes de code est l'outil grep.

Je vais partager avec vous comment utiliser la commande grep sous Linux avec des exemples.

Exemples utiles réels des commandes grep sous Linux

Si vous regardez dans man, vous verrez cette brève description de l'outil grep : "imprimer des lignes correspondant à un motif. »

Cependant, ne vous laissez pas tromper par une définition aussi humble : grep est l'un des outils les plus utiles de la boîte à outils Unix et il existe d'innombrables occasions de l'utiliser dès que vous travaillez avec des fichiers texte.

Il est toujours préférable d’avoir des exemples concrets pour comprendre comment les choses fonctionnent. Je vais donc utiliser l'arborescence des sources Asciidoctor.js pour illustrer certaines des fonctionnalités de grep.

Vous pouvez télécharger cette arborescence source depuis GitHub, et si vous le souhaitez, vous pouvez même consulter le même ensemble de modifications que j'ai utilisé lors de la rédaction de cet article. Cela vous assurera d’obtenir des résultats parfaitement identiques à ceux décrits dans la suite de cet article :

git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1

1. Rechercher toutes les occurrences d'une chaîne (usage de base)

Asciidoctor.js prend en charge le moteur JavaScript Nashorn pour la plateforme Java. Je ne connais pas Nashorn, je pourrais donc profiter de cette occasion pour en savoir plus en explorant les parties du projet faisant référence à ce moteur JavaScript.

Pour commencer, j'ai vérifié s'il y avait des paramètres liés à Nashorn dans le fichier package.json décrivant les dépendances du projet :

linux@handbook:~$ grep nashorn package.json
    "test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",

Oui, apparemment, il y a eu des tests spécifiques à Nashorn. Alors, étudions cela un peu plus.

2. Recherche insensible à la casse dans un ensemble de fichiers

Maintenant, je veux examiner de plus près les fichiers du répertoire ./npm/test/ mentionnant explicitement Nashorn.

Une recherche insensible à la casse (option -i) est probablement meilleure ici puisque je dois trouver les deux références à nashorn et Nashorn (ou tout autre combinaison de caractères majuscules et minuscules) :

linux@handbook:~$ grep -i nashorn npm/test/*.js
npm/test/nashorn.js:const nashornModule = require('../module/nashorn');
npm/test/nashorn.js:log.task('Nashorn');
npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');

En effet, l'insensibilité à la casse s'est avérée utile ici. Sinon, j'aurais raté l'instruction require('../module/nashorn'). Sans doute devrais-je examiner ce dossier plus en détail plus tard.

3. Recherchez tous les fichiers qui ne correspondent pas

Au fait, y a-t-il des fichiers non spécifiques à Nashorm dans le répertoire npm/test/ ? Pour répondre à cette question, nous pouvons utiliser l'option « imprimer les fichiers non correspondants » de grep (option -L) :

sh$ grep -iL nashorn npm/test/*
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

Remarquez comment, avec l'option -L, la sortie de grep a changé pour afficher uniquement les noms de fichiers. Ainsi, aucun des fichiers ci-dessus ne contient la chaîne « nashorn » (quel que soit le cas). Cela ne veut pas dire qu’ils ne sont pas liés d’une manière ou d’une autre à cette technologie, mais au moins, les lettres « n-a-s-h-o-r-n » ne sont pas présentes.

4. Recherche de modèles dans des fichiers cachés et de manière récursive dans des sous-répertoires

Les deux dernières commandes utilisaient un modèle shell glob pour transmettre la liste des fichiers à examiner à la commande grep.

Cependant, cela présente certaines limites inhérentes : l'étoile (*) ne correspondra pas aux fichiers cachés. Il ne correspondra pas non plus aux fichiers (éventuellement) contenus dans les sous-répertoires.

Une solution serait de combiner grep avec la commande find au lieu de s'appuyer sur un modèle shell glob :

# This is not efficient as it will spawn a new grep process for each file
linux@handbook:~$ find npm/test/ -type f -exec grep -iL nashorn \{} \;
# This may have issues with filenames containing space-like characters
linux@handbook:~$ grep -iL nashorn $(find npm/test/ -type f)

Comme je l'ai mentionné dans les commentaires du bloc de code ci-dessus, chaque solution présente des inconvénients.

Concernant les noms de fichiers contenant des caractères d'espacement, je vous laisse enquêter sur l'option grep -z qui, combinée à l'option -print0 de la commande find , peut atténuer ce problème. N’hésitez pas à utiliser la section commentaires à la fin de cet article pour partager vos idées sur ce sujet !

Néanmoins, une meilleure solution utiliserait l'option « récursive » de grep. Avec cette option, vous donnez sur la ligne de commande la racine de votre arbre de recherche (le répertoire de départ) au lieu de la liste explicite des noms de fichiers à examiner.

Avec l'option -r, grep recherchera tous les fichiers du répertoire spécifié, y compris ceux cachés, puis descendra de manière récursive dans n'importe quel sous-répertoire :

linux@handbook:~$ grep -irL nashorn npm/test/npm/
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

En fait, avec cette option, je pourrais également commencer mon exploration un niveau au-dessus pour voir qu'il existe des tests non-npm qui ciblent également Nashorn :

linux@handbook:~$ grep -irL nashorn npm/

Je vous laisse tester cette commande par vous-même pour voir son résultat ; mais à titre indicatif, je peux dire que vous devriez trouver beaucoup plus de fichiers correspondants !

5. Filtrage des fichiers par leur nom (à l'aide d'expressions régulières)

Il semble donc y avoir des tests spécifiques à Nashorn dans ce projet. Puisque Nashorn est Java, une autre question qui pourrait être soulevée serait « Y a-t-il des fichiers sources Java dans le projet mentionnant explicitement Nashorn ? ».

Selon la version de grep que vous utilisez, il existe au moins deux solutions pour répondre à cette question.

La première consiste à utiliser grep pour rechercher tous les fichiers contenant le modèle « nashorn », puis à diriger la sortie de cette première commande vers une deuxième instance grep filtrant les non-java. fichiers source:

linux@handbook:~$ grep -ir nashorn ./ | grep "^[^:]*\.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

La première moitié de la commande devrait maintenant être compréhensible. Mais qu'en est-il de cette partie « ^[\^:]*\\.java » ?

Sauf si vous spécifiez l'option -F, grep suppose que le modèle de recherche est une expression régulière. Cela signifie qu'en plus des caractères simples qui correspondront textuellement, vous avez accès à un ensemble de métacaractères pour décrire des modèles plus complexes. Le modèle que j'ai utilisé ci-dessus correspondra uniquement :

  • ^ le début de la ligne

  • [^:]* suivi d'une séquence de caractères à l'exception des deux-points

  • \. suivi d'un point (le point a une signification particulière en regex, j'ai donc dû le protéger avec une barre oblique inverse pour exprimer que je veux une correspondance littérale)

  • java et suivi des quatre lettres « java. »

En pratique, puisque grep utilisera deux points pour séparer le nom de fichier du contexte, je ne garde que les lignes ayant .java dans la section nom de fichier. Il convient de mentionner que serait correspondre également aux noms de fichiers .javascript. C'est quelque chose que je laisse essayer de résoudre par vous-même si vous le souhaitez.

6. Filtrage des fichiers par leur nom à l'aide de grep

Les expressions régulières sont extrêmement puissantes. Cependant, dans ce cas particulier, cela semble exagéré. Sans parler de la solution ci-dessus, nous passons du temps à examiner tous les fichiers à la recherche du modèle « nashorn » – la plupart des résultats étant rejetés par la deuxième étape du pipeline.

Si vous utilisez la version GNU de grep, ce qui est probable si vous utilisez Linux, vous avez cependant une autre solution avec l'option --include. Cela demande à grep de rechercher uniquement dans les fichiers dont le nom correspond au modèle global donné :

linux@handbook:~$ grep -ir nashorn ./ --include='*.java'
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

7. Trouver des mots

La chose intéressante à propos du projet Asciidoctor.js est qu'il s'agit d'un projet multilingue. À la base, Asciidoctor est écrit en Ruby, donc, pour être utilisable dans le monde JavaScript, il doit être « transpilé » à l'aide d'Opal, un compilateur source à source Ruby vers JavaScript. Une autre technologie que je ne connaissais pas auparavant.

Ainsi, après avoir examiné les spécificités de Nashorn, je me suis assigné la tâche de mieux comprendre l'API Opal. Comme première étape de cette quête, j'ai recherché toutes les mentions de l'objet global Opal dans les fichiers JavaScript du projet. Il peut apparaître dans les affectations (Opal =), l'accès des membres (Opal.) ou peut-être même dans d'autres contextes. Une expression régulière ferait l'affaire. Cependant, encore une fois, grep propose une solution plus légère pour résoudre ce cas d'utilisation courant. En utilisant l'option -w, il correspondra uniquement aux mots, c'est-à-dire aux modèles précédés et suivis d'un caractère autre qu'un mot. Un caractère autre qu'un mot est soit le début de la ligne, soit la fin de la ligne, ou tout caractère qui n'est ni une lettre, ni un chiffre, ni un trait de soulignement :

linux@handbook:~$ grep -irw --include='*.js' Opal .
...

8. colorer la sortie

Je n'ai pas copié le résultat de la commande précédente car il existe de nombreuses correspondances. Lorsque la sortie est aussi dense, vous souhaiterez peut-être ajouter un peu de couleur pour faciliter la compréhension. Si cela n'est pas déjà configuré par défaut sur votre système, vous pouvez activer cette fonctionnalité à l'aide de l'option GNU --color :

linux@handbook:~$ grep -irw --color=auto --include='*.js' Opal .
...

Vous devriez obtenir le même résultat long que précédemment, mais cette fois la chaîne de recherche devrait apparaître en couleur si ce n'était pas déjà le cas.

9. Compter les lignes correspondantes ou les fichiers correspondants

J'ai mentionné à deux reprises que le résultat des commandes précédentes était très long. Combien de temps exactement ?

linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86

Cela signifie que nous avons un total de 86 lignes correspondantes dans tous les fichiers examinés. Cependant, combien de fichiers différents correspondent ? Avec l'option -l, vous pouvez limiter la sortie grep des fichiers correspondants au lieu d'afficher les <lignes. Ainsi, ce simple changement indiquera combien de fichiers correspondent :

linux@handbook:~$ grep -irwl --include='*.js' Opal . | wc -l
20

Si cela vous rappelle l'option -L, pas de surprise : comme c'est relativement courant, les minuscules/majuscules sont utilisées pour distinguer les options complémentaires. -l affiche les noms de fichiers correspondants. -L affiche les noms de fichiers qui ne correspondent pas. Pour un autre exemple, je vous laisse consulter le manuel pour les options -h/-H.

Fermons cette parenthèse et revenons à nos résultats : 86 lignes correspondantes. 20 fichiers correspondants. Cependant, comment sont réparties les lignes correspondantes dans les fichiers correspondants ? Nous pouvons savoir qu'en utilisant l'option -c de grep qui comptera le nombre de lignes correspondantes par fichier examiné (y compris les fichiers avec zéro correspondance) :

linux@handbook:~$ grep -irwc --include='*.js' Opal .
...

Souvent, cette sortie nécessite un post-traitement car elle affiche ses résultats dans l'ordre dans lequel les fichiers ont été examinés, et elle inclut également des fichiers sans aucune correspondance, ce qui ne nous intéresse généralement pas. Ce dernier problème est assez simple à résoudre :

linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$'

En ce qui concerne l'ordre des choses, vous pouvez ajouter la commande sort à la fin du pipeline :

linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n

Je vous laisse consulter le manuel de la commande sort pour connaître la signification exacte des options que j'ai utilisées. N’oubliez pas de partager vos découvertes en utilisant la section commentaires ci-dessous !

10. Trouver la différence entre deux ensembles correspondants

Si vous vous en souvenez, il y a quelques commandes, j'ai recherché le mot « Opale. » Cependant, si je recherche dans le même ensemble de fichiers toutes les occurrences de la chaîne « Opal », j'obtiens une vingtaine de réponses supplémentaires :

linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
linux@handbook:~$ grep -ir --include='*.js' Opal . | wc -l
105

Trouver la différence entre ces deux ensembles serait intéressant. Alors, que sont les lignes contenant les quatre lettres « opale » à la suite, mais où ces quatre lettres ne forment pas un mot entier ?

Il n’est pas si simple de répondre à cette question. Parce que la même ligne peut contenir à la fois le mot Opal ainsi qu'un mot plus grand contenant ces quatre lettres. Mais en première approximation, vous pouvez utiliser ce pipeline :

linux@handbook:~$ grep -ir --include='*.js' Opal . | grep -ivw Opal
./npm/examples.js:  const opalBuilder = OpalBuilder.create();
./npm/examples.js:  opalBuilder.appendPaths('build/asciidoctor/lib');
./npm/examples.js:  opalBuilder.appendPaths('lib');
...

Apparemment, mon prochain arrêt serait d'enquêter sur l'objet opalBuilder, mais ce sera pour un autre jour.

Le dernier mot

Bien sûr, vous ne comprendrez pas l'organisation d'un projet, encore moins l'architecture du code, en émettant simplement quelques commandes grep !

Cependant, je trouve cette commande incontournable pour identifier les repères et les points de départ lors de l'exploration d'une nouvelle base de code.

J'espère donc que cet article vous a aidé à comprendre la puissance de la commande grep et que vous l'ajouterez à votre coffre à outils. Nul doute que vous ne le regretterez pas !

Articles connexes: