GNU/Linux >> Tutoriels Linux >  >> Linux

Comment déboguer les scripts Bash sous Linux et Unix

Le débogage vous aide à corriger les erreurs dans votre programme. Dans cet article, nous aborderons différentes méthodes pour déboguer les scripts bash dans les systèmes d'exploitation Linux et Unix.

Présentation

Au cours de mes premiers jours de programmation, j'ai passé des heures à essayer de trouver l'erreur dans mon code, et à la fin, cela pourrait être quelque chose de simple. Vous avez peut-être aussi été confronté à la même situation.

Savoir utiliser la technique de débogage appropriée vous aiderait à résoudre les erreurs rapidement. Contrairement à d'autres langages comme Python, Java, etc., il n'y a pas d'outil de débogage pour bash où vous pouvez définir des points d'arrêt, parcourir le code, etc.

Certaines fonctionnalités intégrées aident à déboguer les scripts shell bash. Nous allons voir en détail ces fonctionnalités dans les sections à venir.

Trois façons d'utiliser les options de débogage

Lorsque vous souhaitez activer les options de débogage dans vos scripts, vous pouvez le faire de trois manières.

1 . Activez les options de débogage à partir du shell du terminal lors de l'appel du script.

$ bash [ debugging flags ] scriptname

2 . Activez les options de débogage en passant les drapeaux de débogage à la ligne shebang dans le script.

#!/bin/bash [ debugging flags ]

3 . Activez les options de débogage en utilisant le set commande du script.

set -o nounset
set -u

À quoi sert la commande Set ?

L'set command est une commande intégrée du shell qui peut être utilisée pour contrôler les paramètres bash et modifier le comportement bash de certaines manières.

Normalement, vous n'exécuterez pas de commandes set à partir du terminal pour modifier le comportement de votre shell. Il sera largement utilisé dans les scripts shell, soit pour le débogage, soit pour activer le mode bash strict.

$ type -a set
set is a shell builtin

Vous pouvez accéder à la section d'aide de la commande set pour savoir quels indicateurs elle prend en charge et ce que fait chaque indicateur.

$ set --help

Déboguer une partie du script ou du script complet

Avant d'en savoir plus sur les options de débogage, vous devez comprendre que vous pouvez soit déboguer le script entier, soit seulement une certaine partie du code. Vous devez utiliser la commande set pour activer et désactiver les options de débogage.

  • set -<debugging-flag> activera le mode débogage.
  • set +<debugging-flag> désactivera le mode débogage.

Jetez un oeil au code ci-dessous. set -x activera le mode xtrace pour le script et set +x désactivera le mode xtrace. Tout ce qui vient entre set -x et set +x s'exécutera en mode de débogage xtrace.

Vous en apprendrez plus sur le mode xtrace dans la prochaine section. Donc, pour tout indicateur de débogage, la seule chose dont vous devez vous souvenir est, set - activera le mode et set + désactivera le mode.

#!/bin/bash

set -x
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x

touch ${D_OBJECT}/${F_OBJECT}

Échec si aucune variable n'est définie

Lorsque vous travaillez avec des variables dans bash , l'inconvénient est que si nous essayons d'utiliser une variable indéfinie, le script n'échouera pas avec un message d'erreur comme "Variable non définie" . Au lieu de cela, il imprimera une chaîne vide.

Jetez un oeil au code ci-dessous où je reçois l'entrée de l'utilisateur et la stocke dans la variable $OBJECT . J'ai essayé d'exécuter l'opérateur de test (-f et -d ) sur le $OBJECT1 variable qui n'est pas définie.

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Lorsque j'exécute ce code, il aurait dû me renvoyer une erreur, mais ce n'est pas le cas et même le script s'est terminé avec le code de retour zéro.

Pour remplacer ce comportement, utilisez le -u drapeau qui lancera une erreur lorsqu'une variable indéfinie est utilisée.

Je vais exécuter à nouveau le même code avec le mauvais nom de variable, mais cette fois, il lancera une "variable non liée" erreur.

Vous pouvez également définir le -u option en utilisant le set commande ou passez-la comme argument au shebang.

set -u
set -o nounset

(ou)

#! /bin/bash -u

Le mode Xtrace à la rescousse

C'est le mode que j'utilise largement lorsque je débogue des scripts bash pour les erreurs logiques. Xtrace affichera le code ligne par ligne mais avec des paramètres développés.

Dans la section précédente, lorsque j'ai exécuté le code sans le -u flag, il s'est terminé avec succès mais j'attendais la sortie dans le terminal. Maintenant, je peux exécuter le même script en mode xtrace et voir exactement où le problème se produit dans le script.

Jetez un oeil à l'exemple de code suivant.

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Lorsque j'exécute le code ci-dessus, il ne me renvoie aucune sortie.

Pour déboguer ce problème, je peux exécuter le script dans xtrace mode en passant le -x drapeau.

Dans la sortie ci-dessous, vous pouvez voir que les variables sont développées et imprimées. Cela me dit qu'il y a des chaînes vides assignées aux instructions conditionnelles -f et -d . De cette façon, je peux logiquement vérifier et corriger les erreurs.

Le signe plus que vous voyez dans la sortie peut être modifié en définissant le PS4 variables dans le script. Par défaut, la PS4 est définie sur (+ ).

$ echo $PS4
+
$ PS4=" ==> " bash -x debugging.sh

Vous pouvez également définir le mode Xtrace à l'aide de la commande set ou le transmettre comme argument au shebang.

set -x
set -o xtrace

(ou)

#! /bin/bash -x

De même, lors du débogage, vous pouvez rediriger les journaux de débogage Xtrace vers un fichier au lieu de les imprimer sur le terminal.

Jetez un oeil au code ci-dessous. J'attribue un descripteur de fichier 6 au .log fichier et BASH_XTRACEFD="6" redirigera les journaux de débogage xtrace vers le descripteur de fichier 6.

#!/bin/bash


exec 6> redirected_debug.log 
PS4=' ==> ' 
BASH_XTRACEFD="6" 
read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT1 ]]
then
    echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
    echo "$OBJECT is a directory"
fi

Lorsque j'exécute ce code au lieu d'imprimer la sortie xtrace dans le terminal, il sera redirigé vers le .log fichier.

$ cat redirected_debug.log 
==> read -p 'Please provide the object name :  ' OBJECT
==> [[ -f '' ]]
==> [[ -d '' ]]

État de sortie PIPE

Le comportement par défaut lors de l'utilisation d'un tube est qu'il prendra le code de sortie de la dernière commande d'exécution dans le tube. Même si les commandes précédentes du tube ont échoué, le reste du tube sera exécuté.

Jetez un oeil à l'exemple ci-dessous. J'ai essayé d'ouvrir un fichier qui n'est pas disponible et de le canaliser avec un nombre de mots programme. Même si le cat génère une erreur, le programme de comptage de mots s'exécute.

Si vous essayez de vérifier le code de sortie de la dernière commande run pipe en utilisant $? , vous obtiendrez zéro comme code de sortie qui provient du programme de comptage de mots.

$ cat nofile.txt | wc -l
cat: nofile.txt: No such file or directory
0
$ echo $?
0

Lorsque pipefail est activé dans le script, si une commande renvoie un code de retour différent de zéro dans le tube, il sera considéré comme le code de retour pour l'ensemble du pipeline. Vous pouvez activer pipefail en ajoutant la propriété set suivante dans votre script.

set -o pipefail

Il y a toujours un problème avec cette approche. Normalement, ce à quoi on devrait s'attendre est que si une commande dans le tube échoue, le script doit se terminer sans exécuter le reste de la commande dans le tube.

Mais malheureusement, même si une commande échoue, la commande suivante dans le tuyau s'exécute. En effet, chaque commande du tube s'exécute dans son propre sous-shell. Le shell attendra que tous les processus du tube soient terminés, puis renverra le résultat.

Mode strict bash

Pour éliminer toutes les erreurs possibles que nous avons vues dans les sections précédentes, il est recommandé d'ajouter les options suivantes dans chaque script.

Nous avons déjà discuté en détail de toutes ces options dans la section précédente.

  • -e flag => Quitter le script si une commande génère un code de sortie non nul.
  • -u flag => Faire échouer le script si un nom de variable non défini est utilisé.
  • pipefail => Si une commande du pipeline échoue, le code de sortie sera pris en compte pour l'ensemble du pipeline.
  • IFS => Séparateur de champ interne, le définir sur nouvelle ligne (\n) et (\t) fera que la division ne se produira que dans la nouvelle ligne et la tabulation.
set -e
set -u
set -o pipefail

Ou

set -euo pipefail
IFS=$'\n\t'

Capturer des signaux à l'aide de TRAP

Piège vous permet de capturer des signaux vers votre script bash et de prendre certaines mesures en conséquence.

Pensez à un scénario où vous déclenchez le script mais que vous souhaitez annuler le script en utilisant CTRL+C frappe. Dans ce cas, SIGINT sera envoyé à votre script. Vous pouvez capturer ce signal et exécuter certaines commandes ou fonctions.

Jetez un œil au pseudo-code ci-dessous. J'ai créé une fonction appelée nettoyage qui s'exécutera lorsque SIGINT est passé au script.

trap 'cleanup' TERM INT
function cleanup(){
    echo "Running cleanup since user initiated CTRL + C"
    <some logic>
}

Vous pouvez utiliser le trap "DEBUG", qui peut être utilisé pour exécuter une instruction à plusieurs reprises dans le script. La façon dont il se comporte est que chaque instruction qui s'exécute dans l'interruption de script exécutera la fonction ou l'instruction associée.

Vous pouvez comprendre cela en utilisant l'exemple ci-dessous.

#!/bin/bash

trap 'printf "${LINENO} ==> DIR_NAME=${D_OBJECT} ; FILE_NAME=${F_OBJECT}; FILE_CREATED=${FILE_C} \n"' DEBUG

read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT

touch ${D_OBJECT}/${F_OBJECT} && FILE_C="Yes"
exit 0

Il s'agit d'un programme simple qui obtient l'entrée de l'utilisateur et crée un fichier et un répertoire. La commande trap s'exécutera pour chaque instruction du script et imprimera les arguments passés et l'état de création du fichier.

Découvrez la sortie ci-dessous. Pour chaque ligne du script, le trap est déclenché et les variables sont mises à jour en conséquence.

Imprimer le code en mode verbeux

En mode verbeux, le code sera imprimé avant de renvoyer le résultat. Si le programme nécessite une entrée interactive, dans ce cas, seule cette ligne sera imprimée suivie d'un bloc de codes.

Jetez un oeil au programme suivant. C'est un programme simple qui obtient un objet de l'utilisateur et vérifie si l'objet passé est un fichier ou un répertoire en utilisant une instruction conditionnelle .

#!/bin/bash

read -p "Please provide the object name :  " OBJECT
if [[ -f $OBJECT ]]
then
  echo "$OBJECT is a file"
elif [[ -d $OBJECT ]]
then
  echo "$OBJECT is a directory"
fi

Lorsque j'exécute le code ci-dessus, il imprime d'abord le code, puis attend la saisie de l'utilisateur, comme indiqué ci-dessous.

Une fois que j'ai passé l'objet, le reste du code sera imprimé suivi de la sortie.

Vous pouvez également définir le mode verbeux en utilisant set ou en shebang .

set -v
set -o verbose

(ou)

#! /bin/bash -v

Vous pouvez également combiner le mode détaillé avec d'autres modes.

set -vx # Verbose and Xtrace Mode
set -uv # Verbose and Unset Mode

Validation de la syntaxe - mode noexec

Jusqu'à présent, nous avons vu comment travailler avec des erreurs logiques dans le script. Dans cette section, parlons des erreurs de syntaxe.

Les erreurs de syntaxe sont très courantes dans les programmes. Vous avez peut-être manqué une citation ou n'avez pas réussi à sortir de la boucle, etc. Vous pouvez utiliser le "-n " indicateur appelé noexec mode pour valider la syntaxe avant d'exécuter le programme.

Je vais exécuter le morceau de code ci-dessous et valider la syntaxe.

#!/bin/bash

TOOLS=( htop peek tilix vagrant shutter )
for TOOL in "${TOOLS[@]" 
do
    echo "--------------INSTALLING: ${TOOL}---------------------------"
    apt install ${TOOL} -y
#done

Il y a deux erreurs dans ce programme. Tout d'abord, je n'ai pas réussi à fermer les accolades dans la "for loop " et deuxièmement done le mot-clé est commenté, ce qui devrait marquer la fin de la boucle.

Lorsque j'exécute ce programme, j'obtiens les messages d'erreur suivants indiquant qu'il manque des accolades et mot-clé terminé . Parfois, le numéro de ligne pointé dans le message d'erreur ne contiendra aucune erreur que vous devriez rechercher pour trouver l'erreur réelle.

$ bash -n ./debugging.sh 

./debugging.sh: line 6: unexpected EOF while looking for matching `"'
./debugging.sh: line 8: syntax error: unexpected end of file

Il est à noter que, par défaut, lorsque vous exécutez le script, bash validera la syntaxe et lancera ces erreurs même sans utiliser le mode noexec.

Alternativement, vous pouvez également utiliser le set commande ou shebang pour utiliser le noexec mode.

set -n 
set -o noexec

Ou,

#! /bin/bash -n

Il existe quelques outils externes qui valent la peine d'être utilisés pour déboguer les scripts. L'un de ces outils est Shellcheck . Shellcheck peut également être intégré à des éditeurs de texte populaires tels que vscode, sublime text, Atom.

Conclusion

Dans cet article, je vous ai montré quelques-unes des façons de déboguer les scripts bash. Contrairement à d'autres langages de programmation, bash n'a pas d'outils de débogage autres que certaines options intégrées. Parfois, ces options de débogage intégrées seront plus que suffisantes pour faire le travail.

Guides de script bash :

  • Scripts bash :analyse des arguments dans les scripts bash à l'aide de getops
  • Comment créer des boîtes de dialogue GUI dans des scripts Bash avec Zenity sous Linux et Unix
  • Scripts bash – Énoncé de cas
  • Scripts bash – Instructions conditionnelles
  • Scripts bash – Manipulation de chaînes
  • Scripts bash – Explication de la commande Printf à l'aide d'exemples
  • Scripts bash – Tableau indexé expliqué à l'aide d'exemples
  • Scripts bash - Tableau associatif expliqué avec des exemples
  • Scripts bash – Explication de la boucle For avec des exemples
  • Scripts bash :boucle While et Until expliquée à l'aide d'exemples
  • La redirection bash expliquée avec des exemples
  • Scripts bash – Variables expliquées à l'aide d'exemples
  • Scripts bash – Fonctions expliquées à l'aide d'exemples
  • La commande Bash Echo expliquée avec des exemples sous Linux
  • Tutoriel Bash Heredoc pour les débutants

Linux
  1. Comment définir une adresse IP statique et configurer le réseau sous Linux

  2. Comment définir/créer des variables d'environnement et de shell sous Linux

  3. Comment définir, répertorier et supprimer des variables d'environnement sous Linux

  4. UNIX / Linux :Comment installer et configurer mutt

  5. Comment définir définitivement $PATH sous Linux/Unix ?

Comment configurer la clé publique et privée SSH sous Linux

Comment utiliser la commande echo dans les scripts Bash sous Linux

Comment définir la date et l'heure sous Linux

Comment définir et supprimer des variables d'environnement sous Linux

Comment effacer l'historique de Bash sous Linux et Mac

Comment installer et configurer 1Password sur Linux Desktop