Joseph Tux

Bibliothèque de fonctions BASH

Fonctions d’affichage élaboré pour script bash
mercredi 20 février 2013 par Joseph Tux2

Comme dans les autres langages, on peut utiliser des bibliothèques en bash.

Mes fonctions d’affichage assistée pour scripts bash en sont un exemple :
Ces fonctions apportent à celles de la commande « echo » les enrichissement et facilités suivantes :
Couleurs, messages automatiques (titre, N° de ligne, N° du message), sorties commentées, fichiers de log)

Principe

Une bibliothèque en bash ne semble documentée explicitement nulle part dans le man, ni dans les articles, ni dans les livres que j’ai lus.

Mais elle est très simplement réalisable.

Voici ma bibliothèque d’affichages élaborés.

LE PRINCIPE

Dans cet article, on présume qu’ un script bash répond aux exigences courantes suivantes :

  • Il est écrit dans un fichier .
  • Ce fichier est marqué comme exécutable .
  • Il débute par une ligne plus ou moins équivalente à la ligne suivante :
    # !/bin/bash - -
  • Il est appelé par son nom,
    • soit dans un répertoire du PATH ( /usr/local/lib/bash/ , par exemple)
    • soit dans un répertoire désigné ( le répertoire courant ./ , par exemple ) .

Lorsqu’ un script bash est appelé, il est lancé dans un nouveau processus, qui contient son espace de noms pour les fonctions et les variables.

Donc, quand une fonction ou une variable est définie et/ou affectée, elle devient accessible aux commandes de ce script [1]

Au lieu de définir une fonction dans le script ( = à partir de ce processus ), il suffit donc de l’inclure dans ce même processus.
Cela se fait très simplement en « sourçant » un fichier de définitions.
La commande source du bash ( qui s’écrit aussi avec un simple point . ), se contente de lire un fichier texte. Si les lignes lues contiennent des commandes du shell, alors elles sont exécutées.
C’est le cas des définitions de fonctions, des affectations de variables, comme de toute autre ligne de code. Les commentaires restent des commentaires.

INCLUSION

Pas de directive « include library » [2], « from module » [3] ou autre « use module » [4], un simple point suffit.
Bien entendu, il n’y a aucun traitement « automagique », et le nom du fichier de fonctions ( ou de variables pré-déclarées ) doit être donné avec son chemin.

Le chemin courant n’est pas celui du processus père ( celui d’où est appelé le script ), mais bien celui du script lui-même.

J’ai donc décidé de mettre ces fichiers bibliothèques dans le répertoire de mes scripts : /usr/local/bin/

Avec cette règle, il suffit donc d’appeler la bibliothèque dans une ligne [5] du script par un point, puis un espace [6], puis son nom de base :

# !/bin/bash/ —
. MA_BIBLIOTHEQUE

ou

# !/bin/bash/ —
source MA_BIBLIOTHEQUE

Dés lors, les fonctions de votre bibliothèque sont utilisables, accessibles comme si vous les aviez écrites dans votre script.

NOTES :

  • La bibliothèque n’a pas besoin [7] être exécutable .
  • Elle doit être accessible en lecture à l’utilisateur .
  • Elle ne doit évidemment pas contenir d’en-tête du type shebang # !/bin/bash, car elle serait alors lancée dans un autre processus, et donc inutilisable par le script .

Usage

Un exemple avec ma bibliothèque AFFICHE_MESSAGES

Cette bibliothèque propose différents modes d’affichage basées sur la commande echo.
Chacune de ces commandes s’utilise comme une simple commande echo, parfois avec un option [8]

Par exemple :

  1. A='Tux'
  2. DEBUG "la variable '$A' vaut $A"

affiche :
DEBUG 1 ligne:66 la variable ’$A’ vaut TUX
Soit :
En bleu : le mot DEBUG, un numéro d’ordre des lignes DEBUG, le N° de la ligne du script concernée, et en noir, le message


Attention : vous pouvez utiliser la bibliothèque en ligne de commande ; dans ce cas faites très attention : si vous utilisez une fonction qui comporte une commande exit, comme FATAL, vous vous déconnecterez probablement [9] du terminal ou pseudo-terminal.

LA BIBLIOTHÈQUE

Utilisez l’onglet IMPORTER pour télécharger ce code

LA BIBLIOTHÈQUE

Cette bibliothèque fournit les fonctions suivantes pour les scripts bash :
- DEBUG DEBUG ’Un message de déboggage numéroté bleu ciel’ Donne aussi le N° de ligne correspondant
- LOG LOG « Message » « ./fichier.log » est la variante de DEBUG qui écrit aussi le message dans un fichier
- TITRE TITRE « Un titre en violet sur fond noir »
- MESSAGE MESSAGE « Un message en jaune sur fond noir »
- ALERTE ALERTE « Un message en rouge sur fond noir »
- EXIT exit avec un message — 2 présentations possibles :
Syntaxe : EXIT [-a] [texte]
avec option -a , phrase de sortie automatique en jaune, texte ajouté en blanc
sans option , texte libre, en jaune.
- FATAL FATAL « Message » Gestion d’erreur fatale avec sortie du programme
- RESET [10] RESET annule les couleurs et passe à la ligne ;
RESET n annule les couleurs sans passer à la ligne

Il est possible de créer un fichier par fonction ou par groupe de fonctions plus limitées.

  1. ### Cette bibliothèque fournit pour les scripts bash les fonctions d'affichage suivantes:
  2.  
  3. # Elle doit être appelée dans le script par: « . /usr/local/lib/bash/AFFICHE_TOUT »
  4. # le raccourci : . AFFICHE_TOUT est possible si le script et la bibliothèque sont dans le même répertoire
  5.  
  6. #COULEURS
  7. # CE N'EST PAS UNE FONCTION, MAIS UNE DÉCLARATION DE VARIABLES
  8. # Aide visuelle: «bash /home/aide-memoire/bash/COULEURS.txt» ou
  9. # «. /home/aide-memoire/bash/COULEURS.txt»
  10.  
  11. rose='\e[;40;31m'
  12. rouge='\e[;40;31;1m'
  13. vertm='\e[;40;32m'
  14. vert='\e[;40;32;1m'
  15. jaune='\e[;40;33;1m'
  16. bleum='\e[;40;34m'
  17. bleu='\e[;40;34;1m'
  18. mauvem='\e[;40;35m'
  19. mauve='\e[;40;35;1m'
  20. turq='\e[;40;36m' # turquoise
  21. canard='\e[;40;36;1m' #
  22. gris='\e[;40;37m'
  23. blanc='\e[;40;37;1m'
  24. reset='\e[00m' # important
  25.  
  26. # exemple d'utilisation:
  27. # echo -e "$vertm message en vertm $reset"
  28.  
  29. # Les 2 lignes suivantes servent au programmeur:
  30. # elles s'afficheront si l'appel est suivi de l'option «affiche» ( sans tiret ni guillemets etc. )
  31.  
  32. if [[ $1 == affiche ]] ; then
  33. echo -e "Les variables suivantes sont définies:\n\t $rose rose $rouge rouge $vertm vertm $vert vert $jaune jaune $bleum bleum $bleu bleu $mauvem mauvem $mauve mauve $turq turq $canard canard $gris gris $blanc blanc $reset reset"
  34. echo -e "( le suffixe $vertm m $reset = minus, indique une casse de caractère $bleum fine $reset ( contraire de $bleu grasse: bold $reset )"
  35. fi
  36.  
  37. ### DEBUG 'Un message de déboggage numéroté bleu ciel' Donne aussi le N° de ligne correspondant
  38. declare -i COMPTEDEB=1 # pour que += additionne au lieu de concaténer
  39.  
  40. # Syntaxe: DEBUG ["Message"] ["Mot_clé"]
  41. # Par défaut: message est vide et mot_clé vaut «DEBUG»
  42.  
  43. DEBUG () {
  44. MOTCLE=DEBUG
  45. if [[ $2 ]] ; then
  46. MOTCLE="$2"
  47. fi
  48. # un "echo DEBUG" en couleur et numéroté
  49. #DEB=$(echo -en "\e[;36;1mDEBUG ${COMPTEDEB} ligne:${BASH_LINENO}\e[00m")
  50. DEB=$(echo -en "\e[;36;1m$MOTCLE ${COMPTEDEB} ligne:${BASH_LINENO}\e[00m")
  51. echo -e "$DEB $1"
  52. COMPTEDEB+=1 # plus propre
  53. # echo "debug dans DEBUG: $COMPTEDEB" # la variable est bien incrémentée
  54. # mais elle n'est pas persistante, bien que déclarée avant la fonction
  55. # SOLUTION: ce compteur existe abusivement dans les autres fichiers AFFICHE_ !
  56. # qui le réinitialise à 1
  57. } >&2
  58.  
  59. ### ALERTE ALERTE "Un message en rouge sur fond noir"
  60.  
  61. ALERTE () {
  62. # Affichage d' une alerte en couleur (rouge):
  63. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  64. # Les \t ou \n ne sont pas interprétés (affichés ! )
  65. echo -en '\e[40;31;1m'; # Rouge sur fond noir sans retour à la ligne
  66. echo -n "$1" ; # Texte sans retour à la ligne
  67. echo -n "ligne $BASH_LINENO"
  68. echo -e '\e[00m' # sans couleur retour à la ligne
  69. # TODO ajouter ici <echo -n "$2"> pour que $2 puisse être \n (et faire <echo -ne> sur la ligne précédente )
  70. # afin de permettre d'insérer le TITRE au début d'une autre ligne echo, TITRE ou sortie de commande
  71. } >&2 # FIN de ALERTE
  72.  
  73. #-# EXIT TODO inspiré de DEBUG + ALERTE + "exit N° DEBUG" Gestion d'erreur fatale avec sortie du programme
  74.  
  75. # avec option -a , phrase de sortie automatique jaune, texte ajouté, optionnel, blanc
  76. # sans option , texte libre, jaune.
  77.  
  78. EXIT () {
  79. # Affichage en jaune, exit:
  80. if test "$1" == -a
  81. then
  82. shift # se débarasser de «-a»
  83. SORTIE=$(echo -en "\e[40;33;1m SORTIE DU SCRIPT $(basename $0), ligne $BASH_LINENO . \e[00m")
  84. echo -e "$SORTIE $1"
  85. else
  86. SORTIE=$(echo -en "\e[40;33;1m $1 \e[00m")
  87. echo -e "$SORTIE"
  88. fi
  89. exit
  90. } >&2 # FIN de EXIT
  91.  
  92. #-# FATAL TODO inspiré de DEBUG + ALERTE + "exit N° DEBUG" Gestion d'erreur fatale avec sortie du programme
  93.  
  94. FATAL () {
  95. # Affichage en rouge, N° de ligne du message, exit avec le N° d'erreur du script:
  96. FAT=$(echo -en "\e[40;31;1mERREUR FATALE ${COMPTEFAT} ligne:${BASH_LINENO}\e[00m")
  97. echo -e "$FAT $1"
  98. # let COMPTEFAT+=1
  99. # export $COMPTEFAT # Valeur persistante, sinon: non-sens
  100. # exit ${COMPTEFAT}
  101. exit 127 # Le succès de FATAL, c'est l'erreur du script !
  102. } >&2 # FIN de FATAL
  103. #TODO
  104.  
  105. MESSAGE () {
  106. # Affichage d' un titre en couleur (violet):
  107. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  108. # Les \t ou \n ne sont pas interprétés (affichés ! )
  109. # echo -en '\e[40;33;1m'; # Jaune sur fond noir sans retour à la ligne
  110. echo -en '\e[40;32;1m'; # Vert clair sur fond noir sans retour à la ligne
  111. echo -en "$1" ; # Texte sans retour à la ligne
  112. echo -e '\e[00m' # sans couleur retour à la ligne
  113. # TODO ajouter ici <echo -n "$2"> pour que $2 puisse être \n (et faire <echo -ne> sur la ligne précédente )
  114. # afin de permettre d'insérer le TITRE au début d'une autre ligne echo, TITRE ou sortie de commande
  115. } >&2 # FIN de TITRE
  116.  
  117. RESET () {
  118. # Rétablir l'affichage sans couleur
  119. if [ ! $1 ] ; then
  120. echo -e '\e[00m' # retour à la ligne
  121. # appel: RESET n
  122. elif [ $1 == 'n' ] ; then
  123. echo -en '\e[00m' # sans retour à la ligne
  124. else
  125. echo -e '\e[00m' # retour à la ligne
  126. fi
  127. } >&2 # FIN de RESET
  128.  
  129. ### TITRE TITRE "Un titre en violet sur fond noir"
  130.  
  131. TITRE () {
  132. # Affichage d' un titre en couleur (violet):
  133. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  134. # Les \t ou \n ne sont pas interprétés (affichés ! )
  135. echo -en '\e[40;35;1m'; # Violet sur fond noir sans retour à la ligne
  136. echo -en "$1" ; # Texte sans retour à la ligne
  137. echo -e '\e[00m' # sans couleur retour à la ligne
  138. # TODO ajouter ici <echo -n "$2"> pour que $2 puisse être \n (et faire <echo -ne> sur la ligne précédente )
  139. # afin de permettre d'insérer le TITRE au début d'une autre ligne echo, TITRE ou sortie de commande
  140. } >&2 # FIN de TITRE
  141.  
  142. LOG () { # TODO version alpha
  143. # $1 = le message
  144. # $2 = le chemin du fichier de log
  145. # AFFICHE COMME DEBUG ET ÉCRIT DANS UN LOG AVEC tee
  146. ### MESSAGE MESSAGE "Un message en jaune sur fond noir"
  147.  
  148. # vérifier que le chemin de $2 est donné en argument:
  149. if [[ $2 ]] ; then
  150. # Un nom est trouvé
  151. if [[ -f $2 ]] ; then
  152. # le fichier existe
  153. # vérifier que le chemin de $2 est accessible:
  154. if [[ -w $2 ]] ; then
  155. MESSAGE "Le fichier $2 existe déja; les logs seront ajoutés"
  156. AUTOR="OUI"
  157. else
  158. ALERTE "Le fichier $2 n'est pas accessible en écriture."
  159. ALERTE "Les informations (logs) ne seront pas conservées"
  160. fi
  161. else
  162. # sinon il faudra le créer
  163. MESSAGE "Le fichier $2 n'existe pas, il va être créé"
  164. AUTOR="OUI"
  165. fi
  166. else
  167. # Pas de valeur à $2
  168. ALERTE "Le programme $0 ne donne pas de nom de fichier de log"
  169. fi
  170. if [[ $AUTOR==OUI ]] ; then
  171. #DEBUG "Autorisation d'écrire les logs"
  172. DEBTXT=$(echo -en "\e[;36;1mDEBUG ${COMPTEDEB} ligne:${BASH_LINENO}\e[00m")
  173. echo -e "$0 : $1" | tee -a "$2"
  174. COMPTEDEB+=1 # Le compteur de DEBUG
  175. fi
  176. }
  177. <\code>
  178.  
  179.  
  180.  
  181. # SYNTAXE:
  182. # Ces fonctions s'utilisent toutes comme la commande 'echo'
  183. ################################################
  184.  
  185. declare -i COMPTEDEB=1 # i pour que += additionne au lieu de concaténer
  186. DEBUG () {
  187. # un "echo DEBUG" de couleur bleu ciel, numéroté, localisé, puis un message:
  188. DEB=$(echo -en "\e[;36;1mDEBUG ${COMPTEDEB} ligne:${BASH_LINENO}\e[00m")
  189. # Le message est dans "$1":
  190. echo "$DEB $1"
  191. COMPTEDEB+=1
  192. } >&2
  193.  
  194. #
  195.  
  196. TITRE () {
  197. # Affichage d' un titre en couleur (violet):
  198. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  199. # Les \t ou \n ne sont pas interprétés ( il sont affichés )
  200. echo -en '\e[40;35;1m'; # Violet sur fond noir sans retour à la ligne
  201. echo -n "$1" ; # Texte sans retour à la ligne
  202. echo -e '\e[00m' # sans couleur retour à la ligne
  203. } >&2 # FIN de TITRE
  204.  
  205. #
  206.  
  207. MESSAGE () {
  208. # Affichage d' un titre en couleur (violet):
  209. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  210. # Les \t ou \n ne sont pas interprétés (affichés ! )
  211. echo -en '\e[40;33;1m'; # Jaune sur fond noir sans retour à la ligne
  212. echo -n "$1" ; # Texte sans retour à la ligne
  213. echo -e '\e[00m' # sans couleur retour à la ligne
  214. } >&2 # FIN de MESSAGE
  215.  
  216. #
  217.  
  218. ALERTE () {
  219. # Affichage d' une alerte en couleur (rouge):
  220. # Appeler le titre entre apostrophes, sur une ou plusieurs lignes
  221. # Les \t ou \n ne sont pas interprétés (affichés ! )
  222. echo -en '\e[40;31;1m'; # Rouge sur fond noir sans retour à la ligne
  223. echo -n "$1" ; # Texte sans retour à la ligne
  224. echo -e '\e[00m' # sans couleur retour à la ligne
  225. } >&2 # FIN de ALERTE
  226.  
  227. #
  228.  
  229. FATAL () {
  230. # Affichage en rouge, N° de ligne du message, exit avec le N° d'erreur du script:
  231. FAT=$(echo -en "\e[40;31;1mERREUR FATALE ${COMPTEFAT} ligne:${BASH_LINENO}\e[00m")
  232. echo "$FAT $1"
  233. COMPTEFAT+=1
  234. exit ${COMPTEFAT}
  235. } >&2 # FIN de FATAL

Télécharger

ANNULER

Une bibliothèque ANNULE_ a été créée pour chaque fonction, et une pour toutes les fonctions ( ANNULE_TOUT )
Il est alors facile de déactiver un ou plusieurs type d’affichage ( et d’action exit ) pour l’ensemble du script en sourçant une [11] bibliothèque « ANNULE_ » sur une ligne suivant celle de la bibliothèque AFFICHE_ concernée [12].

Pour annuler une fonction [13] il suffit de surcharger sa définition par celle d’une fonction qui ne fait rien.

La fonction DEBUG qui ne fait rien :

DEBUG () {
:
}

est simplement l’appel à la fonction bash qui « ne fait rien », mais, selon le précepte de Raymond DEVOS le fait vraiment, car ne rien faire c’est déjà faire quelque chose

Plus

Hors sujet

  1. la 1re ligne du script appelant se termine par 2 signes « - - » (tiret, moins) accolés :
    #!/bin/bash --
    Dans un script bash, c’est un moyen simple de s’assurer contre le passage d’options indésirables de bash.
  2. Les fonctions de ma bibliothèque sont déclarées avec une redirection vers l’erreur standard, [14] car ce sont toutes des fonctions d’affichage de message sans rapport directe avec ce que votre script voudra afficher comme résultat (sur la sortie standard)
  3. Cette technique permet aussi d’écrire des scripts modulaires .
    Elle est sans doute peu utilisée dans cet esprit, car un script un peu long justifie généralement d’utiliser un autre langage ( Perl, Python etc.. ), mais elle pourrait au moins en faciliter l’écriture (et le débogage) avant d’éventuellement concaténer les différents modules ( un script entête et déclarations, + un ou des modules fonctions, + un module principal , qui appellerait éventuellement ses éléments en fonction des options passées )

Fenêtre graphique

On peut aussi ajouter des fonctions d’affichage — et de saisie interactive — graphiques avec zenity, mais on sort de l’usage de simples utilitaires en ligne de commande, sauf pour épater .. [15]

Exemples :

- Fonction Z_QUESTION

La fonction renvoie vrai ou faux, selon la réponse OUI ou NON choisie
Z_OUI_NON () {
# Il faut au moins un argument:
if [[ $# -eq 0 ]] ; then
   zenity  --question --width=1024 --title "Question}"  --text "Donnez un texte à votre question."
   # Sinon, on sort avec un message
   # on peut choisir de pousuivre le script, dans ce cas remplacer la ligne suivante par un « return » pour sortir de la fonction
   EXIT "Syntaxe: Z_QUESTION "Texte de la question" ["Titre"]
fi
# Ouvrir l'affichage graphique de la question:
zenity  --question --width=1024 --title "${2:=Question}"  --text "$1";
}


- Fonction Z_ENTREE

Z_ENTREE () {
   # La fonction ouvre un formulaire, attend un texte et l'affecte à la variable « entree »
   # Un 2ème argument présente le formulaire («Écrire», par défaut))
   # Un 3éme argument change son titre («Entrez le texte» par défaut )
   # Elle renvoie vrai ou faux selon que l'on a validé ou annulé cette entrée.

# Il faut au moins un argument:
if [[ $# -eq 0 ]] ; then
   zenity  --question --width=1024 --title "Question}"  --text "Donnez un texte à votre question."
   # Sinon, on sort avec un message
   # on peut choisir de pousuivre le script, dans ce cas remplacer la ligne suivante par un « return » pour sortir de la fonction
   EXIT "Syntaxe: Z_QUESTION "Texte de la question" ["Titre"]
fi

   entree=$(zenity  --entry --width=1024 --height=50 --title "${3:=Entrez le texte}" --entry-text "$1" --text "${2:=Écrire} ") && return 0 || return 1;
}

IMPORTER

Vous pouvez importer, diffuser, modifier, utiliser ces documents en respectant les termes de la licence GNU, qui régit les droits de GNU bash

Bibliothèque d’affichage assisté : AFFICHE_TOUT
Toutes mes fonctions d’affichage :
_
- DEBUG
- ALERTE
- EXIT
- FATAL
- MESSAGE
- RESET
- TITRE
- LOG


ANNULE_DEBUG
Un exemple de commande
de neutralisation des commandes
« DEBUG ’’Message’’ »


Script de démonstration
Toutes les fonctions
présentées en 1 écran
il faut installer la bibiothèque
AFFICHE_TOUT
à l’adresse indiquée à la ligne 3
( à votre choix )


Tester la fonction LOG
Un script de démonstration
de la fonction LOG


Affichage des couleurs
Présentation des couleurs pour le terminal
avec leurs codes et la syntaxe pour PS1 et echo
ATTENTION : Version dash ( /bin/sh de DEBIAN )
Pour adapter à bash :
il suffit d’ajouter l’option -e à echo
aux lignes 26 ( 2 fois echo -e ) et 36

[1sous réserve qu’elles ne soient pas elles-même lancées dans un autre processus, comme entre parenthèses ou après un tube (pipe, | ) par exemple

[2C

[3Python

[4Perl

[5au début, ou au moins avant son utilisation

[6comme n’importe quelle autre commande

[7donc ne doit pas

[8« EXIT -a » pour avoir un message automatique en plus du message éventuel ; LOG « message » « ./fichier.log » a besoin d’ un nom de fichier de log !

[9sauf si avez fait une 2e connexion sur la première,avec su par exemple

[10Cette fonction est intéressante avec les variables $ fournies par la bibliothèque AFFICHE_COULEURS

[11ou plusieurs

[12c’est à dire AFFICHE_TOUT ou AFFICHE_.
Un simple commentaire « # » devant cette ligne permet ainsi de réactiver l’ensemble des lignes utilisant cette fonction[[ou ces fonctions

[13et non la supprimer, auquel cas chaque appel dans le script donnerait une erreur fatale

[14>&2

[15les jobards.. , les futurs bidouilleurs, ses proches qui ne comprennent pas ou n’y croient pas, les inconditionnels de la souris etc.