GNU/Linux >> Tutoriels Linux >  >> Linux

Regex et grep :flux de données et blocs de construction

Dans Présentation des expressions régulières , j'ai couvert ce qu'ils sont et pourquoi ils sont utiles. Voyons maintenant plus en détail comment ils sont créés. Parce que GNU grep est l'un des outils que j'utilise le plus (qui fournit une implémentation plus ou moins standardisée des expressions régulières), j'utiliserai cet ensemble d'expressions comme base pour cet article. Nous examinerons ensuite sed (un autre outil qui utilise des expressions régulières) dans un article ultérieur.

Toutes les implémentations d'expressions régulières sont basées sur des lignes. Un modèle créé par une combinaison d'une ou plusieurs expressions est comparé à chaque ligne d'un flux de données. Lorsqu'une correspondance est établie, une action est entreprise sur cette ligne comme prescrit par l'outil utilisé.

Par exemple, lorsqu'une correspondance de modèle se produit avec grep , l'action habituelle consiste à transmettre cette ligne à STDOUT et à supprimer les lignes qui ne correspondent pas au modèle. Comme nous l'avons vu dans Premiers pas avec les expressions régulières :un exemple , le -v L'option annule ces actions, de sorte que les lignes avec des correspondances sont ignorées.

Chaque ligne du flux de données est évaluée individuellement. Considérez chaque ligne de flux de données comme un enregistrement, où les outils qui utilisent des expressions régulières traitent un enregistrement à la fois. Lorsqu'une correspondance est établie, une action définie par l'outil utilisé est effectuée sur la ligne qui contient la chaîne correspondante.

Blocs de construction Regex

Le tableau suivant contient une liste des expressions de bloc de construction de base et des métacaractères implémentés par le GNU grep commande (et la plupart des autres implémentations de regex) et leurs descriptions. Lorsqu'elles sont utilisées dans un modèle, chacune de ces expressions ou métacaractères correspond à un seul caractère dans le flux de données en cours d'analyse :

Expression Description

Caractères alphanumériques

Littéraux

A-Z,a-z,0-9

Tous les caractères alphanumériques et certains caractères de ponctuation sont considérés comme des littéraux. Ainsi la lettre a dans une expression régulière correspondra toujours à la lettre "a" dans le flux de données en cours d'analyse. Il n'y a pas d'ambiguïté pour ces personnages. Chaque caractère littéral correspond à un et un seul caractère.
. (point) Le métacaractère point (.) est la forme d'expression la plus basique. Il correspond à n'importe quel caractère unique dans la position où il se trouve dans un modèle. Donc le motif b.g correspondrait à "big", "bigger", "bag", "baguette" et "bog", mais pas à "dog", "blog", "hug", "lag", "gag", "leg", etc. .

Expression entre parenthèses

[liste de personnages]

GNU grep appelle cela une expression entre parenthèses, et c'est la même chose qu'un ensemble pour le shell Bash. Les crochets entourent une liste de caractères à faire correspondre pour un seul emplacement de caractère dans le modèle. [abcdABCD] correspond aux lettres "a", b", "c" ou "d" en majuscule ou en minuscule. [a-dA-D] spécifie une plage de caractères qui crée la même correspondance. [a-zA-Z] correspond à l'alphabet en majuscules et minuscules.

[:nom de la classe :]

Classes de personnages

Il s'agit d'une tentative POSIX de standardisation des regex. Les noms de classe sont censés être évidents. Par exemple, le [:alnum:] classe correspond à tous les caractères alphanumériques. Les autres classes sont [:digit :] qui correspond à n'importe quel chiffre de 0 à 9, [:alpha:] ,[:space:] , etc. Notez qu'il peut y avoir des problèmes dus aux différences dans les séquences de tri dans différents paramètres régionaux. Lire le grep page de manuel pour plus de détails.

^ et $

Ancres

Ces deux métacaractères correspondent respectivement au début et à la fin d'une ligne. On dit qu'ils ancrent le reste du motif au début ou à la fin d'une ligne. L'expression ^b.g correspondrait uniquement à "big", "bigger", "bag", etc., comme indiqué ci-dessus s'ils se produisent au début de la ligne en cours d'analyse. Le motif b.g$ correspondrait à "big" ou "bag" uniquement s'ils apparaissent à la fin de la ligne, mais pas à "bigger".

Explorons ces blocs de construction avant de continuer avec certains des modificateurs. Le fichier texte que nous utiliserons pour l'expérience 3 provient d'un projet de laboratoire que j'ai créé pour une ancienne classe Linux que j'enseignais. Il était à l'origine dans un fichier odt LibreOffice Writer mais je l'ai enregistré dans un fichier texte ASCII. La plupart des éléments de formatage comme les tableaux ont été supprimés, mais le résultat est un long fichier texte ASCII que nous pouvons utiliser pour cette série d'expériences.

Exemple :entrées de table des matières

Prenons un exemple pour explorer ce que nous venons d'apprendre. Tout d'abord, faites le ~/testing répertoire votre PWD (créez-le si vous ne l'avez pas déjà fait dans l'article précédent de cette série), puis téléchargez l'exemple de fichier à partir du GitHub.

[student@studentvm1 testing]$  wget https://raw.githubusercontent.com/opensourceway/reg-ex-examples/master/Experiment_6-3.txt

Pour commencer, utilisez le less commande pour regarder et explorer le Experiment_6-3.txt fichier pendant quelques minutes pour avoir une idée de son contenu.

Maintenant, utilisons un simple grep expressions pour extraire des lignes du flux de données d'entrée. La table des matières (TOC) contient une liste de projets et leurs numéros de page respectifs dans le document PDF. Extrayons la table des matières en commençant par les lignes se terminant par deux chiffres :

[student@studentvm1 testing]$  grep [0-9][0-9]$ Experiment_6-3.txt

Cette commande n'est pas vraiment ce que nous voulons. Il affiche toutes les lignes qui se terminent par deux chiffres et manque les entrées TOC avec un seul chiffre. Nous verrons comment traiter une expression pour un ou plusieurs chiffres dans une expérience ultérieure. Regarder l'ensemble du fichier en less , nous pourrions faire quelque chose comme ça.

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[0-9]$"

Cette commande est beaucoup plus proche de ce que nous voulons, mais elle n'y est pas tout à fait. Nous obtenons des lignes plus loin dans le document qui correspondent également à ces expressions. Si vous étudiez les lignes supplémentaires et regardez celles du document complet, vous pouvez voir pourquoi elles correspondent tout en ne faisant pas partie de la table des matières.

Cette commande manque également les entrées de la table des matières qui ne commencent pas par "Lab Project". Parfois, ce résultat est le meilleur que vous puissiez faire, et il donne un meilleur aperçu de la table des matières que nous n'avions auparavant. Nous allons voir comment combiner ces deux grep instances en une seule dans une expérience ultérieure.

Maintenant, modifions un peu cette commande et utilisons l'expression POSIX. Notez les doubles accolades ([[]] ) autour :

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[[:digit:]]$"

Les accolades simples génèrent un message d'erreur.

Cette commande donne les mêmes résultats que la tentative précédente.

Exemple :systemd

Cherchons quelque chose de différent dans le même fichier :

[student@studentvm1 testing]$ grep systemd Experiment_6-3.txt

Cette commande répertorie toutes les occurrences de "systemd" dans le fichier. Essayez d'utiliser le -i option pour vous assurer que vous obtenez toutes les instances, y compris celles qui commencent par des lettres majuscules (la forme officielle de "systemd" est entièrement en minuscules). Ou, vous pouvez changer l'expression littérale en Systemd .

Comptez le nombre de lignes contenant la chaîne systemd . J'utilise toujours -i pour s'assurer que toutes les instances de l'expression de recherche sont trouvées quelle que soit la casse :

[student@studentvm1 testing]$ grep -i systemd Experiment_6-3.txt | wc
20      478     3098

Comme vous pouvez le voir, j'ai 20 lignes, et vous devriez avoir le même nombre.

Exemple :Métacaractères

Voici un exemple de correspondance d'un métacaractère :le crochet gauche ([ ). Essayons d'abord sans rien faire de spécial :

[student@studentvm1 testing]$  **grep -i "[" Experiment_6-3.txt**
grep: Invalid regular expression

Cette erreur se produit car [ est interprété comme un métacaractère. Nous devons échapper ce caractère avec une barre oblique inverse (\ ) afin qu'il soit interprété comme un caractère littéral et non comme un métacaractère :

[student@studentvm1 testing]$ grep -i "\[" Experiment_6-3.txt

La plupart des métacaractères perdent leur signification particulière lorsqu'ils sont utilisés à l'intérieur d'expressions entre crochets :

  • Pour inclure un ] littéral , placez-le en premier dans la liste.
  • Pour inclure un ^ littéral , placez-le n'importe où sauf en premier.
  • Pour inclure un [ littéral , placez-le en dernier.

Répétition

Les expressions régulières peuvent être modifiées à l'aide d'opérateurs qui vous permettent de spécifier zéro, une ou plusieurs répétitions d'un caractère ou d'une expression. Ces opérateurs de répétition sont placés immédiatement après le caractère littéral ou le métacaractère utilisé dans le modèle :

Dans les regex, le ? signifie zéro ou une occurrence au plus du caractère précédent. Ainsi, par exemple, drives? correspond à "drive" et "drives" mais pas à "driver". Ce résultat est un peu différent du comportement de ? dans un globe.

Opérateur Description
?
* Le caractère précédant le * sera mis en correspondance zéro ou plusieurs fois sans limite. Dans cet exemple, drives* correspond à "drive", "drives" et "drivesss" mais pas à "driver". Encore une fois, c'est un peu différent du comportement de * dans un glob.
+ Le caractère précédant le + sera apparié une ou plusieurs fois. Le caractère doit exister dans la ligne au moins une fois pour qu'une correspondance se produise. Par exemple, drives+ correspond à "drives" et "drivesss" mais pas à "drive" ou "driver".
{n} Cet opérateur correspond au caractère précédent exactement n fois. L'expression drives{2} correspond à "drivess", mais pas à "drive", "drives", "drivesss" ou à un nombre quelconque de caractères "s" à la fin. Cependant, comme "drivesssss" contient la chaîne drivess , une correspondance se produit sur cette chaîne, donc la ligne serait une correspondance par grep .
{n,} Cet opérateur correspond au caractère précédent n fois ou plus. L'expression drives{2,} correspond "drivess" mais pas "drive", "drives", "drivess", "drives" ou tout nombre de caractères "s" à la fin. Parce que "drivesssss" contient la chaîne drivess , une correspondance se produit.
{,m} Cet opérateur correspond au caractère précédent pas plus de m fois. L'expression drives{,2} correspond à "drive", "drives" et "drivess", mais pas à "drivesss", ni à un nombre quelconque de caractères "s" à la fin. Encore une fois, parce que "drivesssss" contient la chaîne drivess , une correspondance se produit.
{n,m} Cet opérateur correspond au caractère précédent au moins n fois, mais pas plus de m fois. L'expression drives{1,3} correspond à "drives", "drivess" et "drivesss", mais pas à "drivessss" ni à un nombre quelconque de caractères "s" à la fin. Encore une fois, parce que "drivesssss" contient une chaîne correspondante, une correspondance se produit.

À titre d'exemple, exécutez chacune des commandes suivantes et examinez attentivement les résultats afin de comprendre ce qui se passe :

[student@studentvm1 testing]$  **grep -E files? Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives*" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives+" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{,2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,3}" Experiment_6-3.txt**

Assurez-vous d'expérimenter ces modificateurs sur d'autres textes dans le fichier d'exemple.

Modificateurs de métacaractères

Il reste encore quelques modificateurs intéressants et importants que nous devons explorer :

Modificateur Description
< Cette expression spéciale correspond à la chaîne vide au début d'un mot. L'expression <fun correspondrait à "fun" et "Function", mais pas à "refund".
> Cette expression spéciale correspond à l'espace normal ou à la chaîne vide (" ") à la fin d'un mot, ainsi qu'à la ponctuation qui apparaît généralement dans la chaîne à un seul caractère à la fin d'un mot. Donc environment> correspond "environnement", "environnement" et "environnement", mais pas "environnements" ou "environnement".
^ Dans une expression de classe de caractères, cet opérateur annule la liste de caractères. Ainsi, alors que la classe [a-c] correspond à "a", "b" ou "c", à cette position du modèle, la classe [^a-c] correspond à n'importe quoi sauf "a", "b" ou "c".
| Lorsqu'il est utilisé dans une expression régulière, le | métacaractère est un opérateur logique "ou". Il s'appelle officiellement l'infixe ou alternance opérateur. Nous avons déjà rencontré celui-ci dans Initiation aux expressions régulières :un exemple , où nous avons vu que la regex "Team|^\s*$" signifie "une ligne avec 'Team' ou (| ) une ligne vide contenant zéro, un ou plusieurs caractères d'espacement tels que des espaces, des tabulations et d'autres caractères non imprimables."
( and ) Les parenthèses ( and ) nous permettent d'assurer une séquence spécifique de comparaison de modèles, comme cela pourrait être utilisé pour les comparaisons logiques dans un langage de programmation.

Nous avons maintenant un moyen de spécifier les limites des mots avec le \< et \> métacaractères. Cela signifie que nous pouvons maintenant être encore plus explicites avec nos modèles. Nous pouvons également utiliser la logique dans des modèles plus complexes.

Par exemple, commencez par quelques modèles simples. Ce premier sélectionne toutes les instances de drives mais pas drive , drives , ou des caractères "s" de fin supplémentaires :

 [student@studentvm1 testing]$  **grep -Ei "\<drives\>" Experiment_6-3.txt**

Construisons maintenant un modèle de recherche pour localiser les références à tar (la commande d'archivage sur bande) et les références associées. Les deux premières itérations affichent plus que juste tar -lignes liées :

[student@studentvm1 testing]$ grep -Ei "tar" Experiment_6-3.txt
[student@studentvm1 testing]$ grep -Ei "\<tar" Experiment_6-3.txt
[student@studentvm1 testing]$  grep -Ein "\<tar\>" Experiment_6-3.txt

Le -n L'option dans la dernière commande ci-dessus affiche les numéros de ligne pour chaque ligne dans laquelle une correspondance s'est produite. Cette option peut aider à localiser des instances spécifiques du modèle de recherche.

Astuce : Les lignes de données correspondantes peuvent s'étendre au-delà d'un seul écran, en particulier lors de la recherche d'un fichier volumineux. Vous pouvez diriger le flux de données résultant via l'utilitaire less, puis utiliser la fonction de recherche less qui implémente également les expressions régulières pour mettre en évidence les occurrences de correspondances avec le modèle de recherche. L'argument de recherche dans less est :\<tar\> .

Ce modèle suivant recherche "script shell", "programme shell", "variable shell", "environnement shell" ou "invite shell" dans notre document de test. Les parenthèses modifient l'ordre logique dans lequel les comparaisons de modèles sont résolues :

[student@studentvm1 testing]$ grep -Eni "\<shell (script|program|variable|environment|prompt)" Experiment_6-3.txt

Remarque : Cet article est une version légèrement modifiée du chapitre 6 du volume 2 de mon livre Linux, "Using and Administering Linux:Zero to SysAdmin", qui sortira d'Apress fin 2019.

Supprimez les parenthèses de la commande précédente et exécutez-la à nouveau pour voir la différence.

Conclusion

Bien que nous ayons maintenant exploré les blocs de construction de base des expressions régulières dans grep , il existe une variété infinie de façons de les combiner pour créer des modèles de recherche complexes mais élégants. Cependant, grep est un outil de recherche et ne fournit aucune capacité directe pour éditer ou modifier une ligne de texte dans le flux de données lorsqu'une correspondance est établie. Pour cela, nous avons besoin d'un outil comme sed , dont je parle dans mon prochain article.


Linux
  1. Utiliser la commande Grep et localiser ?

  2. Couper / Grep Et Df -h ?

  3. Grep et queue -f ?

  4. Le défi et la promesse du Big Data

  5. Principes de base des Vhosts et des blocs de serveur

Grep Regex :un guide complet

Expressions régulières dans Grep (Regex)

KDE Connect s'améliore de plus en plus

Comment connecter et partager des données entre deux systèmes Linux

Apache Cassandra :fonctionnalités et installation

10 exemples pratiques de regex avec grep