Archives par mot-clé : couleur

Afficher un texte multi-colore dans le terminal

Dans le précédent article, nous avons vu comment mettre en couleur du texte dans le terminal avec la commande printf.

Il serait parfois intéressant de pouvoir distinguer, dans le terminal, des informations plus ou moins « tabulées ». L’objectif de cet article est de présenter une solution de mise en couleurs d’un texte, c’est-à-dire un ensemble de mots séparés par des espaces. Nous allons rapidement constater une limitation liée à ces fameux espaces, ce que nous tenterons de résoudre dans un deuxième temps.

Concentrons-nous d’abord sur la manière de changer de couleur chaque mot d’un texte. Nous avons tout de même une piste : la fonction printcolor réalisée précédemment, à laquelle nous pouvons passer comme paramètre une valeur de couleur comprise entre 0 et 7. Si nous partons du principe que le fond de notre terminal est noir, nous n’utiliserons pas la valeur 0 et commencerons à 1 par conséquent.

Reste à trouver une façon simple de changer de valeur pour chaque nouveau mot de notre texte. Nous pouvons par exemple nous servir d’une boucle for à laquelle nous allons passer le texte en paramètre. Ainsi, pour chaque mot du texte, nous pouvons exécuter notre fonction printcolor. Et dans le même temps, il suffit d’incrémenter une variable initialisée à 1. Si nous détectons que notre variable $couleur vaut 8, alors nous lui réattribuons la valeur 1. La fonction echorainbow ci-dessous répond à cette première analyse.

function echorainbow ()
{
    couleur=1

    for word in $chaine
    do
        printcolor "$couleur" "$word"

        couleur=$((couleur+1))
        test $couleur -eq 8 && couleur=1
    done
}

Après chargement en session de notre script (via la commande source), ce seul exemple devrait déjà faire de votre terminal une vraie discothèque pour geek :

[benoit:~]$ echorainbow "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam."

Mais comme un texte n’est pas non plus ce qui nous intéresse dans un terminal la plupart du temps, nous allons plutôt voir un exemple un peu plus complexe avec la commande ls. Pour un affichage ligne par ligne, il est dans ce cas nécessaire d’utiliser un petit while derrière un pipe… Si vous ne me croyez pas, essayez d’exécuter « echorainbow `ls` » ! Voici le résultat — nettement perfectible ! — de cette commande :

Première version de la fonction echorainbow
Première version de la fonction echorainbow

Nous nous apercevons que les espaces n’ont pas été conservés, ce qui est logique, puisque les espaces sont utilisés comme séparateurs lors de l’utilisation du for. Ajouter un espace derrière chaque mot n’est pas forcément une solution valable, puisque pour des données tabulées comme c’est le cas avec la commande ls, nous serions quand-même susceptibles de perdre l’alignement vertical permis par l’affichage de plusieurs espaces consécutifs !

Nous nous rendons compte qu’il serait beaucoup mieux de pouvoir garder les espaces. Pour ce faire, il devient donc nécessaire de remplacer chaque espace, non pas par un seul caractère, mais par une chaine de caractères, qui ne risque pas d’être rencontrée parmi tous les mots. A l’aide d’un pipe et de la commande sed, nous allons donc changer tout caractère espace, par exemple en "5PAC3". Ne voyez pas là un clin d’oeil au rappeur 2Pac, mon inspiration est surtout venue du 1337 5p34k… Voici la commande sed correspondante :

sed s/’ ‘/’5PAC3’/g

Cela ne suffit pas. Puisque les espaces nous servent de séparateurs de mots, si nous les supprimons, cela revient à ne plus avoir qu’un seul et unique « mot », mais très long. Comme quoi, parfois, plus c’est long, moins c’est bon… Euh… bref… Il nous faut donc séparer nos mots comme précédemment, par exemple en ajoutant un espace à la suite de chaque série d’espaces à conserver (et donc identifiés par notre super chaine « 5PAC »). Là encore, la commande sed va nous être utile. Mais cette fois-ci, c’est en sortie de la précédente commande sed que nous allons mettre un pipe, afin appliquer cette nouvelle transformation. Cette deuxième commande sed s’écrit ainsi (faites bien attention à l’espace derrière le « 1 » !) :

sed s/"((5PAC3)+)"/"1 "/g

Une fois les deux commandes sed mises bout à bout, voilà ce que ça donne :

sed s/’ ‘/’5PAC3’/g | sed s/"((5PAC3)+)"/"1 "/g

Il ne reste plus qu’à appliquer nos deux sed sur la chaine en entrée, puis, pour chaque mot, rechanger les "5PAC3" en espaces (ce qui peut être fait en sortie de la fonction printcolor, dans la boucle for). Revoyons notre fonction echorainbow avec ces nouveaux éléments pris en compte :

function echorainbow ()
{
    esp="5PAC3"
    chaine=`echo "$*" | sed s/' '/$esp/g | sed s/"\(\($esp\)\+\)"/"\1 "/g`

    j=1

    for word in $chaine
    do
        printcolor "$j" "$word" | sed s/$esp/' '/g

        j=$((j+1))
        test $j -eq 8 && j=1
        done
}

Cette fois-ci, l’exécution de l’exemple avec le ls et le while est bien plus convaincante !

Nouvelle version de la fonction echorainbow
Nouvelle version de la fonction echorainbow

Bien-sûr, l’exemple avec ls n’est pas forcément le plus intéressant… Par contre, l’utilisation de cette fonction peut trouver son sens dans le cas de données « chainées », c’est-à-dire de natures diverses et séparées par un séparateur comme « # » ou « ; ». Prenons l’exemple ci-dessous :

1;2;3;hello world     ;   TOTO;0;TITI;TATA;123;321;Lo;rem;  ip;su;m

La lecture de 10, 20 ou 30 lignes comme celle-ci peut devenir rapidement fastidieuse, surtout lorsqu’on recherche une information précise en plein milieu. Dans ce cas, notre fonction echorainbow va nous permettre de bien distinguer chaque partie… Mais à une condition : il va falloir remplacer les « ; » par des espaces… Mais à une condition (hé oui !) : il va falloir remplacer les bons espaces par un autre caractère… Puis re-remplacer tout ça par les caractères de départ, afin de profiter du même affichage, mais en couleur !

Pour ce faire, nous utiliserons (pour une seule ligne, sinon, ne pas oublier de boucler dans un while) quatre commandes sed, les deux premières avant exécution de la fonction echorainbow, les deux autres après. Ce qui donnera dans notre exemple :

[benoit:~]$ chaine=`echo ‘1;2;3;hello world     ;   TOTO;0;TITI;TATA;123;321;Lo;rem;  ip;su;m’ | sed s/’ ‘/#/g | sed s/’;’/’ ‘/g`
[benoit:~]$ echorainbow "$chaine" | sed s/’ ‘/’;’/g | sed s/#/’ ‘/g

Surtout ne dites rien ! Admirez juste votre terminal… :mrgreen:

Creative Commons License
Publié sous licence Creative Commons.

Afficher un texte en couleur dans le terminal

Certaines commandes du terminal permettent de profiter de la couleur. Ainsi, grep ou bien ls facilitent la lecture d’une donnée en l’affichant en rouge, en gras, en surbrillance, souligné, ou tout ça à la fois . La commande printf le permet également (y compris echo sous certaines conditions), c’est ce qui va nous intéresser ici. Les applications sont nombreuses, ne serait-ce que pour faire apparaître en gras et clignotant la moindre valeur alarmante lors de l’exécution de votre script.

Dans le terminal, un caractère a plusieurs propriétés :

  • la couleur de police
  • la couleur de fond
  • le mode d’affichage

Le mode d’affichage est ce qui permettra de mettre le caractère en souligné, en gras, en clignotant, ou bien encore d’inverser les couleurs de police et de fond… Le mode d’affichage n’interviendra pas dans l’écriture de notre fonction, mais son utilisation reste similaire à celle des couleurs. Une petite recherche sur Internet achèvera d’abreuver votre potentielle soif de connaissances.

Les couleurs de police sont des valeurs numériques comprises entre 30 et 37, soit huit couleurs différentes. Pour faire comprendre au terminal qu’une chaine de caractères doit être affichée dans une certaine couleur, nous allons faire précéder cette chaine d’une série de caractères particuliers, encapsulant le nombre d’une couleur. Cette série de caractères est comme suit :

33[31m

Ce truc horrible tout devant, allant de l’anti-slash «  » au crochet ouvrant « [« , correspond à un code-caractère particulier, à savoir celui de Echap. Oui-oui, le même Echap que celui sur la touche tout en haut à gauche sur votre clavier. En rouge, le nombre 31 correspond à la couleur rouge, puis vient un « m » comme pour dire qu’après « lui » commence le « message »…

Afin d’éviter que le terminal n’affiche tout ce qui suivra en rouge, il faut le prévenir qu’à la fin du-dit message, tout doit redevenir comme avant, avec les paramètres par défaut. Derrière notre texte, nous ajouterons donc une suite de caractères similaire :

33[0m

Nous retrouvons le fameux caractère d’échappement ainsi que le « m », sauf qu’entre les deux nous allons saisir un « 0 » (zéro)… comme pour dire que juste après, on remet tout à zéro. Dit comme ça, en fait, c’est super facile !!… Voici l’exemple complet retourné dans le terminal :

[benoit:~]$ printf ‘33[31mHello world33[0mn’
Hello world

Bien-sûr, il est tout à fait possible de mixer les couleurs. Pour ce faire, il suffit de placer le caractère d’échappement et la valeur de couleur avant les caractères, placer un nouveau caractère d’échappement avec une autre couleur, et ainsi de suite autant de fois que souhaité, en terminant par le caractère d’échappement et la valeur « 0 » :

[benoit:~]$ printf ‘33[31mHe33[32mll33[33mo 33[34mwo33[35mrl33[36md33[0mn’
Hello world

Nous avons vu comment fonctionnait la couleur dans le terminal via la commande printf. Tâchons maintenant de créer une fonction qui nous permettra de saisir un texte dans la couleur de notre choix, sans avoir à saisir systématiquement les codes d’échappement si peu pratiques (et moches !)…

Tout d’abord, nous allons déclarer des noms de couleurs dans notre script. Ces variables vont nous permettre deux choses :

  • D’une part, faciliter notre choix des couleurs en n’ayant pas à nous demander quel est le code pour le jaune ou le fuchsia
  • D’autre part, nous pourrons les réutiliser ensuite pour définir la couleur de fond des caractères

Plutôt que définir des valeurs allant de 30 à 37, nous allons les définir de 0 à 7.

Créons ensuite une fonction « printcolor », qui prendra un premier argument compris entre 0 et 7 (donc pour la couleur), et un second argument pour le texte à mettre dans cette couleur. L’écriture des variables « couleurs » et de la fonction « printcolor » correspond à l’exemple ci-dessous.

BLACK=0
RED=1
GREEN=2
YELLOW=3
NAVY=4
PURPLE=5
BLUE=6
WHITE=7

function printcolor ()
{
    printf "\033[3%dm%s\033[0m" "$1" "$2"
}
BLACK=0
RED=1
GREEN=2
YELLOW=3
NAVY=4
PURPLE=5
BLUE=6
WHITE=7

function printcolor ()
{
    printf "\033[3%dm%s\033[0m" "$1" "$2"
}

Une fois notre fonction exécutée, voici le résultat dans le terminal :

[benoit:~]$ printcolor $RED ‘Hello world’
Hello world

Nous avons vu comment mettre en couleur les caractères, maintenant nous allons aussi mettre en couleur le fond. Pour cela, il suffit de modifier légèrement notre script. Juste après l’emplacement du nombre (compris entre 30 et 37) pour la couleur de caractère, un point-virgule sert de séparateur avec un nouveau nombre, celui-là compris entre 40 et 47. Vous comprenez mieux la raison pour laquelle nous avons défini nos variables entre 0 et 7 ?… Non ??? Aïe… 😉

Qui dit « nouvelle valeur », dit « nouvel argument ». Notre fonction fraîchement modifiée, nous lui passerons désormais comme arguments, tout d’abord la couleur du texte comprise entre 0 et 7, puis la couleur du fond également comprise entre 0 et 7, et enfin notre texte. Voici le script complété en conséquence.

BLACK=0
RED=1
GREEN=2
YELLOW=3
NAVY=4
PURPLE=5
BLUE=6
WHITE=7

function printcolor ()
{
    printf "\033[3%d;4%dm%s\033[0m" "$1" "$2" "$3"
}

Sans oublier de rafraîchir la commande « printcolor » via un petit source votre_script.sh, voici le résultat à l’exécution :

[benoit:~]$ printcolor $GREEN $BLACK ‘Follow the white rabbit’
Follow the white rabbit

Hé oui, vous l’avez compris, dans Matrix, ils utilisaient sûrement des terminaux avec nunux ! :mrgreen:

Comme précisé au début, nous n’avons pas abordé les styles d’affichage tels clignotant, gras ou souligné. Une petite recherche sur Internet vous permettra de constater qu’il vous suffit d’ajouter un nouveau paramètre numérique séparé par un point-virgule. Dès lors, il vous sera aisé de modifier le script ci-dessus pour lui passer non pas trois mais quatre arguments.