Le shell Bash ne contient ni débogueur ni même de commandes ou d'instructions spécifiques pour le déboguage. [1] Les erreurs de syntaxe ou de frappe dans les scripts génèrent des messages d'erreur incompréhensibles n'apportant souvent aucune aide pour déboguer un script non fonctionnel.
Exemple 30-1. Un script bugué
#!/bin/bash # ex74.sh # C'est un script buggué. a=37 if [$a -gt 27 ] then echo $a fi exit 0 |
Sortie d'un script:
./ex74.sh: [37: command not found |
Exemple 30-2. Mot clé manquant
#!/bin/bash # missing-keyword.sh: Quel message d'erreur sera généré? for a in 1 2 3 do echo "$a" # done # Requiert le mot clé 'done' mis en commentaire ligne 7. exit 0 |
Sortie d'un script:
missing-keyword.sh: line 10: syntax error: unexpected end of file |
Les messages d'erreur peuvent ne pas tenir compte des lignes de commentaires d'un script lors de l'affichage du numéro de ligne de l'instruction ayant provoqué une erreur de syntaxe.
Que faire si le script s'exécute mais ne fonctionne pas comme vous vous y attendiez? C'est une erreur de logique trop commune.
Exemple 30-3. test24, un autre script bogué
#!/bin/bash # Ceci est supposé supprimer tous les fichiers du répertoire courant contenant #+ des espaces dans le nom. # Cela ne fonctionne pas. Pourquoi? mauvaisnom=`ls | grep ' '` # echo "$mauvaisnom" rm "$mauvaisnom" exit 0 |
Essayez de trouver ce qui ne va pas avec Exemple 30-3 en supprimant les caractères de commentaires de la ligne echo "$badname". Les instructions echo sont utiles pour voir si ce que vous attendiez est bien ce que vous obtenez.
Dans ce cas particulier, rm "$badname" ne donnera pas les résultats attendus parce que $badname ne devrait pas être entre guillemets. Le placer entre guillemets nous assure que rm n'a qu'un seul argument (il correspondra à un seul nom de fichier). Une correction partielle est de supprimer les guillemets de $badname et de réinitialiser $IFS pour contenir seulement un retour à la ligne, IFS=$'\n'. Néanmoins, il existe des façons plus simples de faire cela.
# Bonnes méthodes de suppression des fichiers contenant des espaces dans leur nom. rm *\ * rm *" "* rm *' '* # Merci, S.C. |
Résumer les symptômes d'un script bogué,
Il quitte brutalement avec un message d'erreur de syntaxe (<< syntax error >>), ou
Il se lance bien, mais ne fonctionne pas de la façon attendue (erreur logique, logic error).
Il fonctionne comme vous vous y attendiez, mais a de déplaisants effets indésirables (logic bomb).
Il existe des outils pour déboguer des scripts non fonctionnels
des instructions echo aux points critiques du script pour tracer les variables, ou pour donner un état de ce qui se passe.
utiliser le filtre tee pour surveiller les processus ou les données aux points critiques.
initialiser des paramètres optionnelles -n -v -x
sh -n nomscript vérifie les erreurs de syntaxe sans réellement exécuter le script. C'est l'équivalent de l'insertion de set -n ou set -o noexec dans le script. Notez que certains types d'erreurs de syntaxe peuvent passer à côté de cette vérification.
sh -v nomscript affiche chaque commande avant de l'exécuter. C'est l'équivalent de l'insertion de set -v ou set -o verbose dans le script.
Les options -n et -v fonctionnent bien ensemble. sh -nv nomscript permet une vérification verbeuse de la syntaxe.
sh -x nomscript affiche le résultat de chaque commande, mais d'une façon abrégée. C'est l'équivalent de l'insertion de set -x ou set -o xtrace dans le script.
Insérer set -u ou set -o nounset dans le script le lance, mais donne un message d'erreur unbound variable à chaque essai d'utilisation d'une variable non déclarée.
Utiliser une fonction << assert >> pour tester une variable ou une condition aux points critiques d'un script. (Cette idée est empruntée du C.)
Exemple 30-4. Tester une condition avec un << assert >>
#!/bin/bash # assert.sh assert () # Si la condition est fausse, { #+ sort du script avec un message d'erreur. E_PARAM_ERR=98 E_ASSERT_FAILED=99 if [ -z "$2" ] # Pas assez de paramètres passés. then return $E_PARAM_ERR # Pas de dommages. fi noligne=$2 if [ ! $1 ] then echo "Mauvaise assertion: \"$1\"" echo "Fichier \"$0\", ligne $noligne" exit $E_ASSERT_FAILED # else (sinon) # return (retour) # et continue l'exécution du script. fi } a=5 b=4 condition="$a -lt $b" # Message d'erreur et sortie du script. # Essayer de configurer la "condition" en autre chose #+ et voir ce qui se passe. assert "$condition" $LINENO # Le reste du script s'exécute si assert n'échoue pas. # Quelques commandes. # ... echo "Cette instruction s'exécute seulement si \"assert\" n'échoue pas." # ... # Quelques commandes de plus. exit 0 |
piéger la sortie.
La commande exit d'un script déclenche un signal 0, terminant le processus, c'est-à-dire le script lui-même. [2] Il est souvent utilisé pour récupérer la main lors de exit, en forçant un << affichage >> des variables, par exemple. Le trap doit être la première commande du script.
Spécifie une action à la réception d'un signal; aussi utile pour le déboguage.
trap '' 2 # Ignore l'interruption 2 (Control-C), sans action définie. trap 'echo "Control-C désactivé."' 2 # Message lorsque Control-C est utilisé. |
Exemple 30-5. Récupérer la sortie
#!/bin/bash trap 'echo Liste de Variables --- a = $a b = $b' EXIT # EXIT est le nom du signal généré en sortie d'un script. a=39 b=36 exit 0 # Notez que mettre en commentaire la commande 'exit' ne fait aucune différence, # car le script sort dans tous les cas après avoir exécuté les commandes. |
Exemple 30-6. Nettoyage après un Control-C
#!/bin/bash # logon.sh: Un script rapide mais sale pour vérifier si vous êtes déjà connecté. VRAI=1 JOURNAL=/var/log/messages # Notez que $JOURNAL doit être lisible (chmod 644 /var/log/messages). FICHIER_TEMPORAIRE=temp.$$ # Crée un fichier temporaire "unique", en utilisant l'identifiant du processus. MOTCLE=adresse # A la connexion, la ligne "remote IP address xxx.xxx.xxx.xxx" # ajoutée à /var/log/messages. ENLIGNE=22 INTERRUPTION_UTILISATEUR=13 VERIFIE_LIGNES=100 # Nombre de lignes à vérifier dans le journal. trap 'rm -f $FICHIER_TEMPORAIRE; exit $INTERRUPTION_UTILISATEUR' TERM INT # Nettoie le fichier temporaire si le script est interrompu avec control-c. echo while [ $VRAI ] # Boucle sans fin. do tail -$VERIFIE_LIGNES $JOURNAL> $FICHIER_TEMPORAIRE # Sauve les 100 dernières lignes du journal dans un fichier temporaire. # Nécessaire, car les nouveaux noyaux génèrent beaucoup de messages lors de la # connexion. search=`grep $MOTCLE $FICHIER_TEMPORAIRE` # Vérifie la présence de la phrase "IP address", # indiquant une connexion réussie. if [ ! -z "$search" ] # Guillemets nécessaires à cause des espaces possibles. then echo "En ligne" rm -f $FICHIER_TEMPORAIRE # Suppression du fichier temporaire. exit $ENLIGNE else echo -n "." # l'option -n supprime les retours à la ligne de echo, # de façon à obtenir des lignes de points continues. fi sleep 1 done # Note: Si vous modifiez la variable MOTCLE par "Exit", # ce script peut être utilisé lors de la connexion pour vérifier une déconnexion # inattendue. # Exercice: Modifiez le script, suivant la note ci-dessus, et embellissez-le. exit 0 # Nick Drage suggère une autre méthode. while true do ifconfig ppp0 | grep UP 1> /dev/null && echo "connecté" && exit 0 echo -n "." # Affiche des points (.....) jusqu'au moment de la connexion. sleep 2 done # Problème: Appuyer sur Control-C pour terminer ce processus peut être # insuffisant (des points pourraient toujours être affichés). # Exercice: Corrigez ceci. # Stephane Chazelas a lui-aussi suggéré une autre méthode. CHECK_INTERVAL=1 while ! tail -1 "$JOURNAL" | grep -q "$MOTCLE" do echo -n . sleep $CHECK_INTERVAL done echo "On-line" # Exercice: Discutez les avantages et inconvénients de chacune des méthodes. |
![]() | trap '' SIGNAL (deux apostrophes adjacentes) désactive SIGNAL pour le reste du script. trap SIGNAL restaure la fonctionnalité de SIGNAL. C'est utile pour protéger une portion critique d'un script d'une interruption indésirable. |
trap '' 2 # Le signal 2 est Control-C, maintenant désactivé. command command command trap 2 # Réactive Control-C |
[1] | Le débogueur Bash de Rocky Bernstein comble légèrement ce manque. |
[2] | Par convention, signal 0 est affecté à exit. |
Précédent | Sommaire | Suivant |
Des Zéros et des Nulls | Niveau supérieur | Options |