36.2. Scripts d'appel

Un « script d'appel » (wrapper) est un script shell incluant une commande système ou un utilitaire, qui accepte et passe un ensemble de paramètres à cette commande. [121] 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 'commandes' ou awk 'commandes'. 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 36.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 36.2. Un script d'appel légèrement plus complexe

#!/bin/bash

#  "subst", un script qui remplace un motif par 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-motif nouveau-motif nom-fichier"
  exit $E_MAUVAISARGS
fi

ancien_motif=$1
nouveau_motif=$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_motif/$nouveau_motif/g" $nom_fichier
#  Bien sûr, 's' est la commande de substitut dans sed,
#+ et /motif/ appelle la correspondance d'adresse.
#  "g" ou l'option globale est la cause de la substitution pour *toute*
#+ occurence de $ancien_motif 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 36.3. Un script d'appel générique qui écrit dans un fichier de traces

#!/bin/bash
#  Generic shell wrapper that performs an operation
#+ and logs it.

# Must set the following two variables.
OPERATION=
#         Can be a complex chain of commands,
#+        for example an awk script or a pipe . . .
LOGFILE=
#         Command-line arguments, if any, for the operation.


OPTIONS="$@"


# Log it.
echo "`date` + `whoami` + $OPERATION "$@"" >> $LOGFILE
# Now, do it.
exec $OPERATION "$@"

# It's necessary to do the logging before the operation.
# Why?

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

#!/bin/bash
# pr-ascii.sh: Prints a table of ASCII characters.

START=33   # Range of printable ASCII characters (decimal).
END=127    # Will not work for unprintable characters (> 127).

echo " Decimal   Hex     Character"   # Header.
echo " -------   ---     ---------"

for ((i=START; i<=END; i++))
do
  echo $i | awk '{printf("  %3d       %2x         %c\n", $1, $1, $1)}'
# The Bash printf builtin will not work in this context:
#     printf "%c" "$i"
done

exit 0


#  Decimal   Hex     Character
#  -------   ---     ---------
#    33       21         !
#    34       22         "
#    35       23         #
#    36       24         $
#
#    . . .
#
#   122       7a         z
#   123       7b         {
#   124       7c         |
#   125       7d         }


#  Redirect the output of this script to a file
#+ or pipe it to "more":  sh pr-asc.sh | more

Exemple 36.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 « à tout faire », il existe une espèce de couteau suisse nommée Perl. Perl combine les capacités de sed et de awk, en y associant un large sous-ensemble de C, pour démarrer. Il est modulaire et contient le support de pratiquement tout ce qui est connu en partant de la programmation orientée objet jusqu'à l'évier de cuisine y compris. Les petits scripts Perl vont eux-mêmes s'intégrer dans des scripts Shell, et on peut penser que Perl est capable de remplacer totalement les scripts Shell - cependant l'auteur du guide ABS reste sceptique à ce sujet.

Exemple 36.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 36.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 depuis la partie Perl du script.
               

Voici un exemple intéressant de wrapper (emballage) du shell, par Martin Matusiak : script undvd. Ce script fournit une interface simple, en ligne de commande, à l'utilitaire complexe mencoder. Autre exemple, par Itzchak Rehberg's Ext3Undel, jeu de scripts permettant de retrouver un fichier effacé sur un système de fichiers ext3.



[121] 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/bin/xmkmf.