Archives par mot-clé : tput

Afficher un texte centré dans le terminal

Pour afficher un titre dans le terminal, on sera tenté de faire un simple « echo ‘                avec plein d’espaces devant' ». Cette solution étant quand-même très limitée (et moche, aussi !), un printf va nous permettre de faire mieux… Reste à régler le problème de la largeur du terminal, qui est déterminante dans le centrage de notre titre.

Dans le précédent article, nous avons vu l’utilisation de la commande tput. Celle-ci va nous être utile encore une fois, mais avec une subtile différence : au lieu d’utiliser directement la valeur qu’elle retourne, celle-ci sera directement employée dans un calcul.

Lorsqu’on veut centrer un texte sur une ligne, on a besoin de connaître la longueur du texte ainsi que la longueur totale de la ligne. Ici, la longueur de la ligne correspond très logiquement à la largeur de notre terminal, que la commande tput nous permet de trouver. Pour le texte, nous passerons par une variable, puisqu’il est très simple d’en connaître sa longueur avec la notation ${#…}.

Qui dit « centre », dit « distance divisée par deux ». Il est donc nécessaire de connaître le milieu du terminal, mais aussi le milieu de notre texte. Un schéma (merci GeoGebra !) peut aider à comprendre le calcul géométrique qui va suivre. Rassurez-vous, ça ne casse pas non plus trois pattes à un canard…

Centrer un texte
Centrer un texte

Considérons que le segment AB correspond à notre terminal, et le segment DE au texte que nous souhaitons centrer. Le point C correspond à la fois au milieu de AB et au milieu de DE. Nous constatons que pour centrer convenablement le texte avec la commande printf, il nous faut calculer la longueur AE, c’est-à-dire la moitié du terminal à laquelle on ajoute la moitié du texte, soit :

AE = AC + CE
AE = AB / 2 + DE / 2

C’est ce calcul que nous retrouvons dans la fonction « printcenter » ci-dessous. Attention toutefois à bien comprendre que les divisions réalisées produisent des nombres entiers ! Hé oui, 5 / 2 = 2…

function printcenter ()
{
    chaine="$*"
    cols=$(($(tput cols) / 2 + ${#chaine} / 2))
    printf "%${cols}s" "$chaine"
}

Cette première fonction est déjà satisfaisante. Toutefois, on peut encore l’améliorer… Pour centrer notre texte, la commande printf complète à gauche avec des espaces, puis s’arrête juste après le texte : finalement, notre texte est justifié à droite d’une longueur AE inférieure à celle du terminal, et derrière, hé bien il n’y a absolument rien… Nous allons compléter ce vide par d’autres espaces afin d’atteindre le bout de la ligne.

Si nous reprenons notre schéma, nous constatons que ces espaces vont correspondre à la distance EB. Celle-ci se traduit par ce calcul :

EB = AB – AE

Or nous connaissons déjà ces longueurs ! Dans notre précédente fonction, il nous suffit alors de créer une seconde variable « cols » en utilisant la première. Il ne reste plus qu’à écrire une chaine d’espaces de la longueur trouvée, juste après le texte justifié à droite.

function printcenter ()
{
    chaine="$*"
    cols1=$(($(tput cols) / 2 + ${#chaine} / 2))
    cols2=$(($(tput cols) - ${cols1}))
    printf "%${cols1}s%${cols2}s\n" "$chaine" " "
}

Cette nouvelle version permet maintenant d’obtenir notre texte centré, avec des espaces avant et après sur toute la longueur du terminal, et surtout quelle que soit la longueur du terminal !

[benoit:~]$ printcenter hello world
                                                            hello world

Répéter un caractère sur toute la largeur du terminal

Un terminal comme Konsole ou Gnome Terminal est une fenêtre redimensionnable. Si l’on souhaite afficher une ligne de séparation sur toute la largeur de celui-ci, il est nécessaire de connaître le nombre de colonnes (ou de caractères) qu’il affiche, forcément variable. Une manière de connaître les dimensions du terminal consiste à lancer la commande stty : celle-ci affiche alors un nombre important d’informations, dont le nombre de colonnes du terminal en cours. Une autre manière de récupérer le nombre de colonnes, consiste plus simplement à lancer la commande tput avec pour argument « cols », dont voici un exemple :

[benoit:~]$ tput cols
237

En encapsulant, en Bash, la commande tput dans $(…), lors de l’exécution cette pseudo-variable sera automatiquement remplacée par sa valeur, 237 dans notre exemple. Voyons maintenant comment nous en servir avec printf pour écrire 237 caractères.

Sans entrer dans les détails de la commande printf, le fait d’écrire printf "%237s" " " permet d’afficher 237 espaces. Puisque notre terminal ne fera pas toujours 237 caractères de large, remplaçons cette valeur par $(tput cols) comme expliqué au-dessus.

Puisque nous souhaitons afficher un élément de séparation dans notre terminal, la série d’espaces devra être remplacée par une série d’autre chose. L’utilisation du « _ » (underscore, souligné, ou «tiret du 8») permettra de simuler une ligne tirée de bout en bout, mais le caractère « * » (étoile ou astérisque), moyennant un « petit ajustement » (ou plutôt un échappement), aurait tout aussi bien fait l’affaire. La commande sed va nous servir à remplacer chacun des espaces par un underscore, via un pipe (le fameux « | ») en sortie du printf. La fonction « printline » ci-dessous prend en compte l’ensemble des points abordés, à savoir l’utilisation de tput, printf et sed.

function printline ()
{
    printf "%$(tput cols)s\n" "" | sed s/' '/"_"/g
}

Cet exemple est déjà un bon début. Maintenant, nous souhaiterions pouvoir afficher une ligne avec n’importe quel caractère, sans avoir à modifier à chaque fois notre script. Pour ce faire, nous allons donc prévoir une variable, laquelle reposera sur le premier argument passé au script. La variable $c à la place du underscore permet de remplacer chaque espace par le caractère (ou les caractères, mais dans ce cas il y aura autant de lignes que de caractères !) passé en argument. Une dernière petite astuce va nous permettre d’utiliser un underscore par défaut. L’écriture ${c:=_} s’explique ainsi : si ma variable $c ne retourne aucun caractère, alors utiliser « _ ». L’exemple ci-dessous reprend la fonction précédemment écrite avec ces quelques améliorations.

function printline ()
{
    c=$1
    printf "%$(tput cols)s" "" | sed s/' '/"${c:=_}"/g | cut -c1-$(tput cols)
}

Cette fonction permet d’obtenir ces différents résultats :

[benoit:~]$ printline
________________________________________________________________________

[benoit:~]$ printline *
************************************************************************

[benoit:~]$ printline Abc
AbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAbcAb