L'objectif de cet article est de vous donner les moyens de mettre en évidence très simplement des fragments de texte en les colorisant avec la commande hl (disponible sur GitHub), après les avoir spécifiés au moyen d'expressions régulières. Les configurations disponibles dans les fichiers de configuration vous permettront d'utiliser cette commande directement pour des besoins courants, mais quelques rappels sur les expressions régulières vous aideront à développer vos propres configurations, vous permettant par exemple de coloriser facilement vos fichiers de logs ou résultats de commandes.
Introduction
La commande hl est une commande système, écrite en langage C, permettant de coloriser de 42 couleurs différentes des fragments de textes lus sur son entrée standard et provenant de fichiers ou de résultats de commandes. Les directives de colorisation sont données au moyen d'expressions régulières et d'options de spécifications de couleurs associées.
1. Téléchargement et génération de la commande hl
Tout d'abord, nous allons voir comment télécharger et générer la commande hl afin de pouvoir la tester au fur et à mesure de la lecture de cet article.
Cette commande est régénérable à partir des sources disponibles sur http://github.com/mbornet-hl/hl.
Il est nécessaire pour cela de disposer des commandes suivantes :
- gcc (compilateur C) ;
- lex (générateur d'analyseurs lexicaux) ;
- make (commande de génération) ;
- git (logiciel de versionnement).
Après avoir récupéré les sources de la commande hl au moyen de la commande :
$ git clone https://github.com/mbornet-hl/hl
se placer dans le répertoire hl/src, et lancer la commande make :
$ cd hl/src; make
Vous pouvez ensuite, si vous le souhaitez, copier la commande hl ainsi générée dans un répertoire de commandes accessibles à tous les utilisateurs :
# cp hl /usr/local/bin
2. Intérêt de la colorisation
La fonction principale de cette commande n'étant que la colorisation, on pourrait se demander si elle est réellement utile et s'il y a un intérêt à consommer du temps CPU pour une simple colorisation de texte. S'agit-il seulement une question d'esthétique ? Pas du tout : il s'agit d'utiliser les capacités de notre cerveau à distinguer très rapidement les couleurs pour repérer des messages spécifiques potentiellement importants ou des valeurs particulières. De même qu'un logiciel de supervision utilise les couleurs pour indiquer les états (vert = normal, jaune ou orange = avertissement, rouge = critique), la commande hl peut être utilisée pour valider des données ou signaler des états ou des franchissements de seuils, comme nous le verrons dans la suite de cet article. L'intérêt majeur de cette commande réside dans le fait qu'elle permet à un utilisateur d'obtenir des gains de temps significatifs lors de l'analyse de données, car la mise en évidence grâce à la colorisation permet de cibler de façon quasi immédiate les informations importantes, sans avoir à parcourir visuellement l'intégralité des caractères affichés, le travail de sélection et de comparaison étant effectué par le programme et non par l'utilisateur. D'autre part, sa grande simplicité d'utilisation en fait une commande qu'on n'hésite pas à utiliser pour coloriser toutes sortes de résultats de commandes.
Voici une copie d'écran permettant d'illustrer ces propos :
Figure 1
Nous allons maintenant découvrir de façon progressive les différentes possibilités d'utilisation de la commande hl.
3. Mise en évidence de chaînes de caractères
La commande hl peut tout d'abord être utilisée pour mettre en évidence, dans un flux de caractères lus sur son entrée standard, un texte spécifié en argument, comme dans la commande suivante :
$ ls -C /bin | hl grep
Figure 2
Ici, chaque chaîne de caractères grep est colorisée en jaune vif et vidéo inversée, qui est la couleur par défaut lorsque l'on ne spécifie qu'une chaîne de caractères pour argument, en omettant toute option de spécification de couleur.
4. Utilisation d'une autre couleur
Si la couleur par défaut ne vous convient pas pour quelque raison que ce soit, il est possible de la remplacer en utilisant la variable d'environnement HL_DEFAUT et en lui assignant une valeur constituée d'une valeur d'intensité (facultative) :
- 1 (dim : sombre) ;
- 2 (normal : normal) ;
- 3 (bright : brillant) ;
et d'un spécificateur de couleur :
- r (pour red : rouge) ;
- g (pour green : vert) ;
- y (pour yellow : jaune) ;
- b (pour blue : bleu) ;
- m (pour magenta : magenta) ;
- c (pour cyan : cyan) ;
- w (pour white : blanc) ;
ou l'une de ces mêmes lettres en majuscules (R, G, Y, B, M, C, W) pour un affichage en vidéo inversée.
Pour coloriser en cyan en vidéo inversée (C) brillant (3), on utilisera donc la directive suivante :
$ export HL_DEFAULT=3C
5. Utilisation de deux couleurs
Si l'on souhaite coloriser deux chaînes de deux couleurs différentes, on n'utilisera pas la syntaxe allégée de mise en évidence d'une chaîne ou d'une expression régulière, mais la syntaxe complète (que nous allons bientôt découvrir) qui permet de spécifier une multitude de couleurs et d'expressions.
Pour l'instant, supposons que nous souhaitions coloriser la chaîne start en vert et la chaîne stop en rouge dans le texte suivant se trouvant dans le fichier exemple.log :
start : 07:00:00
stop : 10:14:22
On utilisera la commande :
$ cat exemple.log | hl -g start -r stop
qui produira le résultat suivant :
Figure 3
6. Choix parmi 42 couleurs
Comme nous l'avons vu dans un paragraphe précédent, nous pouvons spécifier 7 couleurs de base (rouge, vert, jaune, bleu, magenta, cyan, blanc) en 3 intensités (sombre, normal, brillant) et en 2 modes d'affichage (normal et vidéo inversée), ce qui nous donne 7 x 3 x 2 = 42 colorations différentes.
6.1 Options de couleurs
La commande hl dispose d'options permettant de spécifier des couleurs :
- -r : red (rouge) ;
- -g : green (vert) ;
- -y : yellow (jaune) ;
- -b : blue (bleu) ;
- -m : magenta (magenta) ;
- -c : cyan (cyan) ;
- -w : white (blanc).
Les options en majuscules (-R, -G, -Y, -B, -M, -C, -W) désignent les mêmes couleurs, mais en vidéo inversée (reverse video).
6.2 Options d'intensité
Elle dispose également d'options permettant de spécifier le niveau d'intensité des couleurs :
- -1 : dim (sombre)
- -2 : normal (normal)
- -3 : bright (brillant)
Toutes ces options combinées donnent les 42 possibilités suivantes :
Figure 4
Chaque option de spécification de couleur doit être associée à une expression régulière permettant la sélection du texte à coloriser, et peut être utilisée pour différentes expressions régulières, autant de fois que nécessaire.
7. Syntaxe de la commande hl
La syntaxe de la commande est la suivante :
hl: version 1.66
Usage: hl [-h|-H|-V|-[[%.]eiuvdDEL1234][-[rgybmcwRGYBMCWn] regexp ...][--config_name ...] ]
-h : help
-H : help + configuration names
-V : version
-v : verbose
-u : do not bufferize output on stdout
-e : extended regular expressions
-i : ignore case
-E : print on stderr
-r : red
-g : green
-y : yellow
-b : blue
-m : magenta
-c : cyan
-w : white
-R : red (reverse video)
-G : green (reverse video)
-Y : yellow (reverse video)
-B : blue (reverse video)
-M : magenta (reverse video)
-C : cyan (reverse video)
-W : white (reverse video)
-n : never colorize
-%c : specifies the beginning of a range colorized in color 'c'
-. : specifies the end of the previous range
-d : debug
-D : display regular expressions
-L : lex debug
-1 : color brightness (half-bright)
-2 : color brightness (normal : default)
-3 : color brightness (bright)
-4 : color brightness (underscore)
Ces informations sont affichables à l'aide de la commande suivante :
$ hl -h
ou consultables dans la page de manuel [1], affichable par l'une des commandes suivantes :
$ man hl
$ man 1 hl
(après avoir copié le fichier hl.1 dans le répertoire /usr/share/man/man1).
Les options de couleurs sont utilisables autant de fois que nécessaire pour une même exécution de la commande. Les arguments qui suivent les options de spécification de couleurs peuvent être des expressions régulières basiques, étendues, ou de simples chaînes de caractères sans aucun métacaractère.
Il n'y a pas de limites, autres que celles du système, au nombre d'expressions régulières que l'on peut passer à hl, chaque expression régulière étant associée à une option de couleur parmi les 42 couleurs disponibles.
8. Utilisation d'expressions régulières
Les arguments qui suivent les options de spécifications de couleurs sont, par défaut, des expressions régulières basiques. On peut utiliser la syntaxe des expressions régulières étendues en plaçant l'option -e devant les options de spécification de couleurs. Pour éviter les confusions entre les métacaractères des expressions régulières et les métacaractères du shell qui va interpréter la ligne de commandes hl (c'est-à-dire le nom de la commande suivi de ses arguments), il est indispensable de désactiver leur interprétation par le shell en les faisant précéder d'un anti-slash ( \ ) ou en les plaçant entre quotes (simples ou doubles en fonction des besoins).
9. Rappels sur la syntaxe des expressions régulières
Pour les lecteurs qui ne seraient pas familiers avec les expressions régulières, voici un bref rappel les concernant. Pour plus d'informations, se reporter, par exemple, aux documents [2] et [3].
Les expressions régulières sont une notation utilisant des caractères particuliers (appelés métacaractères) et permettant de décrire des ensembles de chaînes de caractères de façon générique.
Pour les expressions régulières basiques :
- le caractère . désigne n'importe quel caractère ;
- les caractères [ et ] permettent de sélectionner un ensemble de caractères définis en extension ou par plages ;
- le caractère * est un facteur de répétition indiquant que ce qui précède peut être répété N fois, N étant positif ou nul ;
- le caractère ^ désigne un début de ligne ;
- le caractère $ désigne une fin de ligne ;
- les caractères \< et \> désignent respectivement un début et une fin de mot.
Pour les expressions régulières étendues :
- les métacaractères des expressions régulières basiques sont disponibles et ont la même signification ;
- le caractère + est un facteur de répétition indiquant que ce qui précède peut être répété N fois, N étant strictement positif ;
- le caractère ? indique que l'élément qui précède est optionnel ;
- le caractère | désigne un OU logique entre deux expressions ;
- les caractères ( et ) permettent de grouper des expressions (et de capturer des sous-chaînes dans le cas de la commande hl) ;
- les caractères { et } permettent de spécifier un facteur de répétition avec minimum et maximum : {min,max} .
10. Colorisation d'une correspondance
Nous avons vu dans un paragraphe précédent comment coloriser les chaînes fixes start et stop, mais supposons que nous voulions aussi coloriser les mots contenant les chaînes start et stop, comme par exemple : starting, started, stopping, stopped, et cela en minuscules et majuscules.
Nous avons plusieurs façons de procéder. La première serait de lister toutes les chaînes fixes, et de passer chacune d'entre elles à l'option de couleur correspondante :
$ hl -g start -g starting -g started -r stop -r stopping -r stopped
mais il faudrait aussi ajouter toutes les combinaisons possibles comportant des caractères majuscules, comme Start, Started, STOP, Stopping, STOPPED...
Ce qui ne serait ni efficace, ni raisonnable (trop de combinaisons possibles, donc trop de risques d'erreurs ou d'oublis).
La commande hl dispose d'une option permettant d'ignorer la casse des lettres ( -i), c'est-à-dire de traiter les lettres de la même façon, qu'elles soient majuscules ou minuscules.
Pour le traitement des extensions des mots recherchés, ce sont les expressions régulières étendues qui vont nous apporter une solution, car elles vont nous permettre d'écrire des expressions génériques comme :
start(|ing|ed) et stop(|ping|ped)
La ligne de commandes hl va donc se limiter à :
$ hl -ei -g '(start(|ing|ed))' -r '(stop(|ping|ped))'
ce qui est tout de même plus court que la ligne de commandes précédente.
11. Colorisation entre deux correspondances
Il existe des cas où l'on souhaite coloriser le texte figurant entre deux balises, comme par exemple entre un BEGIN et un END, un do et un done, un marqueur de début de paragraphe et une ligne vide indiquant une fin de paragraphe, etc.
La commande hl sait également traiter ce cas, grâce à une syntaxe propre aux marqueurs de début et marqueurs de fin.
La syntaxe est la suivante :
-%color 'regexp_debut' -. 'regexp_fin'
où color est constitué d'une valeur d'intensité (facultative) et d'une valeur de couleur, comme par exemple :
-%3g
Exemple de configuration (nous verrons les configurations dans la suite de cet article) :
fdisk :
-e
-2b 'Device Boot.*'
-3w 'Disk.*(/dev/sd.)'
-2
-c '.*/dev/sda.*'
-%c 'Disk /dev/sda'
-. '^$'
-g '.*/dev/sdb.*'
-%g 'Disk /dev/sdb'
-. '^$'
Figure 5
12. Priorité des expressions régulières selon l'ordre de spécification des arguments
Dans l'hypothèse où une chaîne du texte lue correspondrait à plusieurs expressions régulières, celle qui sera effectivement utilisée pour la colorisation sera la première à correspondre dans l'ordre de passage des arguments sur la ligne de commandes de hl: la priorité de colorisation est décroissante au fur et à mesure du parcours des arguments de la ligne de commandes.
La commande suivante affiche en vert les adresses IP valides, et en rouge les adresses IP non valides (dans notre exemple : celles comportant des valeurs sur 3 chiffres supérieures à 255) :
$ hl -e -g '\<((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))\>' -r '\<(([0-9]{1,3}\.){3}[0-9]{1,3})\>'
La première expression ne sélectionne qu'un sous-ensemble de l'ensemble sélectionné par la deuxième expression, mais elle est prioritaire devant cette dernière, car elle est placée avant.
Si les deux expressions avaient été permutées (ainsi que leurs options de couleurs), aucune adresse IP n'aurait pu être colorisée en vert, car toutes les adresses IP, valides comme non valides, auraient correspondu à la première expression.
13. Nécessité d'alléger la syntaxe
On voit bien avec l'exemple précédent qu'une telle syntaxe, si elle peut être utilisée dans un script, interdit l'usage interactif, car elle ferait perdre beaucoup trop de temps à l'utilisation.
Il était donc indispensable d'inventer une syntaxe simple et efficace pour que cette commande puisse durablement être utilisée de façon interactive. Il a donc été choisi d'utiliser un fichier de configuration permettant la définition de différentes configurations et une syntaxe simple pour sélectionner une configuration donnée parmi celles définies dans le fichier.
Contrairement aux commandes Linux standard, cette syntaxe ne fait pas appel à une option pour spécifier la configuration à utiliser. La syntaxe, similaire à celle des options longues, se limite à deux tirets suivis du nom de la configuration choisie. L'intérêt de cette syntaxe réside plus dans le fait que l'utilisateur n'a pas d'option à mémoriser que dans l'économie d'un caractère à taper. Deux fichiers de configurations sont utilisables : le premier ($HOME/.hl.cfg) est propre à chaque utilisateur et prioritaire sur le deuxième (/etc/default/hl) qui est commun à tous les utilisateurs.
La syntaxe des fichiers de configurations est donnée par la page de manuel [4] accessible à l'aide de la commande suivante (après avoir recopié le fichier hl.5 dans /usr/share/man/man5) :
$ man 5 hl
Il est, de plus, possible de combiner plusieurs configurations dans une même configuration ou ligne de commandes, comme par exemple dans la configuration suivante :
errors :
-e
--error
--warning
--remark
En utilisant les fichiers de configuration, on pourra coloriser le résultat de la commande df de la façon suivante :
$ df -h | hl --df
14. Colorisation facile de commandes choisies (script hl_generic)
Malgré la simplification de la syntaxe de la commande hl, la colorisation des commandes n'est pas encore transparente pour l'utilisateur, qui doit toujours rediriger le résultat de ses commandes à coloriser vers la commande hl via un tube de communication interprocessus (pipe).
Le script hl_generic a justement été conçu dans le but de permettre une plus grande transparence dans la colorisation, pour les commandes qui le permettent. Le principe est le suivant : pour toute commande devant être colorisée, on crée un lien sur le script hl_generic, placé dans un répertoire (par exemple hl_bin) dont le chemin d'accès (pathname) est, dans la variable PATH, situé devant le chemin d'accès du répertoire contenant la commande à coloriser. Lors de l'appel à la commande, c'est en fait le lien vers le script hl_generic qui sera trouvé puis exécuté. Ce dernier exécutera la commande d'origine (avec tous les arguments passés) et redirigera ses sorties (stdout et stderr) vers deux processus différents qui coloriseront les données en se basant sur une configuration du nom de la commande, définie dans l'un des fichiers de configuration cités précédemment.
Le contenu de la variable PATH pourra être le suivant :
PATH="/home/mb/hl_bin:/home/mb/bin:/usr/local/bin:/usr/bin:/bin"
Voici ce que peut être le contenu d'un répertoire hl_bin :
Figure 6
15. Désactivation de la colorisation des commandes colorisées
Si l'on souhaite désactiver la colorisation des commandes colorisées, comme par exemple la commande df, il suffit d'initialiser la variable d'environnement USE_HL à la valeur no, de l'une des deux façons suivantes :
$ USE_HL=no df -h
$ export USE_HL=no
$ df -h
16. Gestion des seuils
La technique de sélection des adresses IP valides utilisée dans un paragraphe précédent peut être utilisée pour sélectionner des entiers compris dans un intervalle donné, et donc pour gérer des seuils en définissant des expressions régulières correspondant à des plages de valeurs.
Si l'on veut par exemple sélectionner les entiers compris entre 1975 et 2038, on décomposera les plages valides de la façon suivante :
- de 1975 à 1979 ;
- de 1980 à 1999 ;
- de 2000 à 2029 ;
- de 2030 à 2038 ;
ce qui permettra d'écrire les expressions régulières suivantes :
- '\<197[5-9]\>'
- '\<19[89][0-9]\>'
- '\<20[0-2][0-9]\>'
- '\<203[0-8]\>'
que l'on peut condenser en une seule expression régulière étendue :
'\<(197[5-9]|19[89][0-9]|20[0-2][0-9]|203[0-8])\>'
Pour coloriser des valeurs de pourcentages, on pourra, sur le même principe regrouper les entiers compris entre 0 et 100 en différentes plages, et attribuer à chaque plage une couleur associée à l'expression régulière décrivant les nombres qu'elle contient.
La configuration de la commande df disponible dans le fichier /etc/default/hl est une illustration de cette gestion de seuils.
17. Contrôle de validité de données
Lorsque l'on souhaite valider des données, on peut vouloir coloriser les données valides d'une couleur donnée, par exemple en vert, et les autres, non valides, d'une autre couleur, par exemple en rouge.
Grâce à la priorité des expressions régulières les unes par rapport aux autres en fonction de l'ordre de spécification sur la ligne de commandes, il est possible de le faire simplement grâce à la commande hl.
Si nous souhaitons par exemple valider une liste d'adresses IP, il suffit de spécifier en premier l'expression correspondant aux adresses IP valides, puis spécifier que tout le reste est non valide, ce qui peut être effectué grâce à la configuration suivante :
validate_IP :
-e
-2g '\<((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))\>'
-3r '.+'
18. Colorisation des champs
Il est également possible avec la commande hl de coloriser les champs d'un texte délimités par un séparateur, ou par un ensemble de séparateurs. Si nous prenons le cas du fichier /etc/passwd, le séparateur de champs est le caractère ':' . Il est donc très simple d'attribuer des couleurs spécifiques pour chaque champ en utilisant les expressions régulières. On peut par exemple coloriser le fichier /etc/passwd en utilisant une même couleur pour un champ donné, indépendamment de son contenu, avec la configuration suivante :
passwd :
-e
-c '^[^:]+'
-m '^[^:]+:([^:]+):'
-y '^[^:]+:[^:]+:([^:]+:[^:]+):'
-c '^([^:]*:){4}([^:]+)'
-g '^([^:]*:){5}([^:]+)'
-y '^([^:]*:){6}([^:]+)'
L'ordre de définition de ces options est important, car l'utilisation de parenthèses pour grouper les termes sert aussi implicitement à capturer les chaînes correspondantes pour les coloriser, or dans le cas des trois dernières lignes, on ne souhaite pas coloriser les chaînes correspondant à la première expression entre parenthèses, mais uniquement celles qui correspondent à la deuxième expression entre parenthèses.
Dans le cas ci-dessus, la priorité des expressions nous permet de régler le problème de confusion entre les parenthèses qui servent à grouper les termes ou à capturer les chaînes.
19. Option pour ne pas coloriser
Nous avons vu jusqu'à présent le moyen de coloriser différentes chaînes correspondant à des expressions régulières, mais il existe des cas où l'on souhaiterait conserver parmi les données des chaînes non colorisées, par exemple parce qu'elles peuvent ne pas être importantes dans le cas où un élément de la ligne de données a une valeur souhaitée, mais qu'elles le seront dans le cas contraire. On peut vouloir afficher en rouge tous les champs d'une table dont un champ a une valeur incorrecte, tandis qu'on ne colorisera que le champ concerné si sa valeur est correcte.
La commande hl possède une option pour ce genre de cas : il s'agit de l'option -n, qui s'utilise de la même façon qu'une option de spécification de couleur.
20. Autre intérêt de la commande hl
hl peut également aider à améliorer sa connaissance des expressions régulières de façon ludique, car on peut l'utiliser pour faire rapidement des tests élémentaires ayant un résultat visuel immédiat. Il existe différentes façons de procéder pour lancer la commande et pour injecter les données :
- la commande hl peut être appelée depuis un terminal en mode interactif, ou depuis un script ;
- ses arguments peuvent être passés en ligne de commandes, ou dans un fichier de configuration ;
- les données peuvent être envoyées depuis le clavier, depuis un fichier, ou depuis une commande.
Conclusion
La commande hl est une commande de colorisation extrêmement simple à utiliser et à configurer (pour peu que l'on sache utiliser les expressions régulières). Elle peut rendre de grands services en facilitant et en accélérant l'analyse de données au format texte.
Une fois qu'on a commencé à l'utiliser, il est très difficile de s'en passer.
Références
[1] hl(1) : page de manuel de la commande hl, https://github.com/mbornet-hl/hl/blob/master/man1/hl.1, disponible dans le fichier hl/man1/hl.1 téléchargé depuis GitHub
[2] « Expressions régulières, syntaxe et mise en œuvre » - Martial Bornet - Éditions ENI - ISBN 978-2-7460-9806-0
[3] regex(7) : page de manuel décrivant la syntaxe des expressions régulières
[4] hl(5) : page de manuel des fichiers de configuration de la commande hl, https://github.com/mbornet-hl/hl/blob/master/man5/hl.5, disponible dans le fichier hl/man1/hl.5 téléchargé depuis GitHub
Pour contacter l'auteur de l'article (et de la commande hl) : man.flashnux@gmail.com