33.3. Scripts d'appel

Un « script d'appel » (wrapper) est un script shell qui inclut une commande système ou un utilitaire, qui sauvegarde un ensemble de paramètres passés à cette commande. [101] Intégrer un script dans une ligne de commande complexe simplifie son appel. Ceci est vraiment utile avec sed et awk.

Un script sed ou awk est normalement appelé à partir de la ligne de commande par un sed -e <replaceable>'commandes'</replaceable> ou awk <replaceable>'commandes'</replaceable>. Intégrer ce type de script dans un script Bash permet de l'appeler plus simplement et le rend « réutilisable ». Ceci autorise aussi la combinaison des fonctionnalités de sed et awk, par exemple pour renvoyer dans un tuyau, la sortie d'un ensemble de commandes sed vers awk. Comme un fichier exécutable sauvé, vous pouvez alors l'appeler de manière répétée dans sa forme originale ou modifiée, sans les inconvénients d'avoir à le retaper sur la ligne de commande.

Exemple 33.1. Script d'appel

#!/bin/bash

# C'est un simple script supprimant les lignes blanches d'un fichier.
# Pas de vérification des arguments.
#
# Vous pouvez ajouter quelque chose comme ça :
# E_SANSARGS=65
# if [ -z "$1" ]
# then
#  echo "Usage : `basename $0` fichier-cible"
#  exit $E_SANSARGS
# fi


# Identique à
#    sed -e '/^$/d' nomfichier
# appelé à partir de la ligne de commande.

sed -e /^$/d "$1"
#  Le '-e' signifie qu'une commande d'"édition" suit (optionnel ici).
#  '^' est le début de la ligne, '$' en est la fin.
#  Ceci correspond aux lignes n'ayant rien entre le début et la fin de la ligne.
#  'd' est la commande de suppression.

#  Mettre entre guillemets l'argument de la ligne de commande permet de saisir
#+ des espaces blancs et des caractères spéciaux dans le nom du fichier.

#  Notez que ce script ne modifie pas réellement le fichier cible.
#  Si vous avez besoin de le faire, redirigez sa sortie.

exit 0

Exemple 33.2. Un script d'appel légèrement plus complexe

#!/bin/bash

#  "subst", un script qui substitue un modèle pour un autre dans un fichier,
#+ c'est-à-dire "subst Smith Jones lettre.txt".

ARGS=3             # Le script nécessite trois arguments.
E_MAUVAISARGS=65   # Mauvais nombre d'arguments passé au script.

if [ $# -ne "$ARGS" ]
# Teste le nombre d'arguments du script (toujours une bonne idée).
then
  echo "Usage : `basename $0` ancien-modele nouveau-modele nom-fichier"
  exit $E_MAUVAISARGS
fi

ancien_modele=$1
nouveau_modele=$2

if [ -f "$3" ]
then
    nom_fichier=$3
else
        echo "Le fichier \"$3\" n'existe pas."
    exit $E_MAUVAISARGS
fi

#  Voici où se trouve le vrai boulot.
sed -e "s/$ancien_modele/$nouveau_modele/g" $nom_fichier
#  Bien sûr, 's' est la commande de substitut dans sed,
#+ et /modele/ appelle la correspondance d'adresse.
#  "g" ou l'option globale est la cause de la substitution pour *toute*
#+ occurence de $ancien_modele sur chaque ligne, pas seulement la première.
#  Lisez les documents sur 'sed' pour une explication en profondeur.

exit 0    # Appel avec succès du script qui renvoie 0.

Exemple 33.3. Un script d'appel générique qui écrit dans un fichier de traces

#!/bin/bash
#  Emballage générique qui réalise une opération et la trace.

# Doit configurer les deux variables suivantes.
OPERATION=
#         Peut-être une chaîne complexe de commandes,
#+        par exemple un script awk ou un tube...
JOURNAL=
#         Arguments en ligne de commande, au cas où, pour l'opération.


OPTIONS="$@"


# La tracer.
echo "`date` + `whoami` + $OPERATION "$@"" >> $JOURNAL
# Maintenant, l'exécuter.
exec $OPERATION "$@"

# Il est nécessaire de tracer avant d'exécuter l'opération.
# Pourquoi ?

Exemple 33.4. Un script d'appel autour d'un script awk

#!/bin/bash
# pr-ascii.sh : affiche une table de caractères ASCII.

DEBUT=33   # Liste de caractères ASCII affichables (décimal).
FIN=125

echo " Décimal   Hex     Caractère"   # En-tête.
echo " -------   ---     ---------"

for ((i=DEBUT; i<=FIN; i++))
do
  echo $i | awk '{printf("  %3d       %2x         %c\n", $1, $1, $1)}'
# Le printf intégré de Bash ne fonctionnera pas dans ce contexte :
#     printf "%c" "$i"
done

exit 0


#  Décimal   Hex     Caractère
#  -------   ---     ---------
#    33       21         !
#    34       22         "
#    35       23         #
#    36       24         $
#
#    . . .
#
#   122       7a         z
#   123       7b         {
#   124       7c         |
#   125       7d         }


#  Redirigez la sortie de ce script vers un fichier
#+ ou l'envoyez via un tube dans "more" :  sh pr-asc.sh | more

Exemple 33.5. Un script d'appel autour d'un autre script awk

#!/bin/bash

# Ajoute une colonne spécifiée (de nombres) dans le fichier cible.

ARGS=2
E_MAUVAISARGS=65

if [ $# -ne "$ARGS" ] # Vérifie le bon nombre d'arguments sur la ligne de
                      # de commandes.
then
   echo "Usage : `basename $0` nomfichier numéro_colonne"
   exit $E_MAUVAISARGS
fi

nomfichier=$1
numero_colonne=$2

# Passer des variables shell à la partie awk du script demande un peu d'astuces.
#  Une méthode serait de placer des guillemets forts sur la variable du script
#+ Bash à l'intérieur du script awk.
#     $'$BASH_SCRIPT_VAR'
#      ^                ^
# C'est fait dans le script awk embarqué ci-dessous.
# Voir la documentation awk pour plus de détails.

# Un script multi-ligne awk est appelé par awk : ' ..... '


# Début du script awk.
# -----------------------------
awk '

{ total += $'"${numero_colonne}"'
}
END {
     print total
}     

' "$nomfichier"
# -----------------------------
# Fin du script awk.


#   Il pourrait ne pas être sûr de passer des variables shells à un script awk
#+  embarqué, donc Stephane Chazelas propose l'alternative suivante :
#   ---------------------------------------
#   awk -v numero_colonne="$numero_colonne" '
#   { total += $numero_colonne
#   }
#   END {
#       print total
#   }' "$nomfichier"
#   ---------------------------------------


exit 0

Pour ces scripts nécessitant un seul outil qui-fait-tout, il existe une espèce de couteau suisse nommée Perl. Perl combine les capacités de sed et awk, et y ajoute un grand sous-ensemble de fonctionnalités C. Il est modulaire et contient le support de pratiquement tout ce qui est connu en commençant par la programmation orientée. Des petits scripts Perl vont eux-mêmes s'intégrer dans d'autres scripts, et il existe quelques raisons de croire que Perl peut totalement remplacer les scripts shells (bien que l'auteur de ce document reste sceptique).

Exemple 33.6. Perl inclus dans un script Bash

#!/bin/bash

# Les commandes shell peuvent précéder un script Perl.
echo "Ceci précède le script Perl embarqué à l'intérieur de \"$0\"."
echo "==============================================================="

perl -e 'print "Ceci est un script Perl embarqué.\n";'
# Comme sed, Perl utilise aussi l'option "-e".

echo "==============================================================="
echo "Néanmoins, le script peut aussi contenir des commandes shell et système."

exit 0

Il est même possible de combiner un script Bash et un script Perl dans le même fichier. Dépendant de la façon dont le script est invoqué, soit la partie Bash soit la partie Perl sera exécutée.

Exemple 33.7. Combinaison de scripts Bash et Perl

#!/bin/bash
# bashandperl.sh

echo "Bienvenue dans la partie Bash de ce script."
# Plus de commandes Bash peuvent suivre ici.

exit 0
# Fin de la partie Bash de ce script.

# =======================================================

#!/usr/bin/perl
# Cette partie du script doit être appelé avec l'option -x.

print "Bienvenue de la partie Perl de ce script.\n";
# Plus de commandes Perl peuvent suivre ici.

# Fin de la partie Perl de ce script.

bash$ bash bashandperl.sh
Bienvenue de la partie Bash du script.


bash$ perl -x bashandperl.sh
Bienvenue de la partie Perl du script.
              


[101] Un assez grand nombre d'outils Linux sont, en fait, des scripts d'appel. Quelques exemples parmi d'autres : /usr/bin/pdf2ps, /usr/bin/batch et /usr/X11R6/bin/xmkmf.