Retour au cours

projet cli outil

Objectifs

  • Structurer un script Bash complexe en utilisant des fonctions.
  • Créer un squelette de script réutilisable qui inclut :
    • Le “mode strict” (set -euo pipefail).
    • Le parsing d’arguments avec getopts.
    • Une fonction d’aide (usage).
    • Une fonction pour gérer les erreurs.
    • Une fonction main comme point d’entrée.

Explications détaillées

Notions clés

Lorsque vos scripts dépassent quelques dizaines de lignes, il devient essentiel de leur donner une structure pour qu’ils restent lisibles, maintenables et robustes. Ce “squelette” est une convention que vous pouvez réutiliser pour presque tous les outils en ligne de commande que vous écrirez.

La structure d’un script robuste

  1. Shebang et set : La base de la sécurité et de la prévisibilité.
  2. Documentation : Un bloc de commentaires en haut qui explique le but du script, son usage et les options qu’il accepte.
  3. Fonctions utilitaires : Des petites fonctions pour les tâches répétitives, comme afficher l’aide (usage()) ou mourir proprement sur une erreur (die()).
  4. Parsing des arguments : Un bloc while getopts pour gérer les options.
  5. La fonction main() : Le cœur de la logique du script. Cette convention permet de lire le script de haut en bas : on voit d’abord la configuration et les fonctions, puis le point d’entrée principal tout à la fin.

Le squelette de script

Voici un squelette complet et commenté que vous pouvez utiliser comme point de départ pour vos propres projets.

#!/usr/bin/env bash

# Active le mode strict pour des scripts plus sûrs
set -euo pipefail

# --- Documentation et Constantes ---
# Description:
#   Ce script est un squelette pour un outil en ligne de commande robuste.
#
# Usage:
#   ./mon_outil.sh [-v] [-f <fichier>] <argument_obligatoire>
#
# Options:
#   -v          Active le mode verbeux.
#   -f <fichier> Spécifie un fichier de sortie.
#
readonly SCRIPT_NAME="$(basename "$0")"


# --- Fonctions ---

# Affiche un message d'erreur et quitte
die() {
  echo >&2 "[ERREUR] $@"
  exit 1
}

# Affiche l'aide du script
usage() {
  echo "Usage: $SCRIPT_NAME [-v] [-f <fichier>] <argument_obligatoire>"
  echo ""
  echo "Options:"
  echo "  -v          Active le mode verbeux"
  echo "  -f <fichier> Spécifie un fichier de sortie"
  echo "  -h          Affiche cette aide"
  exit 0
}

# Fonction principale contenant la logique du script
main() {
  # --- Initialisation et parsing des arguments ---
  local VERBOSE=false
  local OUTPUT_FILE=""

  while getopts ":hvf:" opt; do
    case "$opt" in
      h) usage ;;
      v) VERBOSE=true ;;
      f) OUTPUT_FILE="$OPTARG" ;;
      \?|:) die "Option invalide ou argument manquant. Utilisez -h pour l'aide." ;;
    esac
  done
  shift $((OPTIND - 1)) # Supprime les options parsées

  # --- Validation des arguments restants ---
  if [[ $# -ne 1 ]]; then
    usage
  fi
  local ARG_OBLIGATOIRE="$1"


  # --- Logique principale du script ---
  if [[ "$VERBOSE" == true ]]; then
    echo "Mode verbeux activé."
  fi

  echo "Argument obligatoire : $ARG_OBLIGATOIRE"

  if [[ -n "$OUTPUT_FILE" ]]; then
    echo "La sortie sera dans : $OUTPUT_FILE"
    # echo "Contenu..." > "$OUTPUT_FILE"
  else
    echo "Sortie sur stdout."
    # echo "Contenu..."
  fi

  echo "Script terminé avec succès."
}


# --- Point d'entrée ---
# On appelle la fonction main avec tous les arguments du script
main "$@"

Bonnes pratiques

  • Découpez votre logique en fonctions. Chaque fonction doit avoir une seule responsabilité.
  • Utilisez une fonction main. Cela rend le point de départ de votre script explicite.
  • Gérez les erreurs gracieusement. Utilisez une fonction die ou error pour afficher des messages clairs sur stderr et quitter avec un code d’erreur.

Pièges courants

  • Mettre tout le code au niveau global. Sans fonctions, un script devient vite un plat de spaghettis illisible dès qu’il dépasse une vingtaine de lignes.
  • Ne pas valider les arguments. Un script qui fait confiance à ses arguments est un script qui plantera de manière inattendue.

Exercices

  1. Adoptez le squelette :

    • Copiez-collez le squelette ci-dessus dans un nouveau fichier mon_outil.sh et rendez-le exécutable.
    • Lancez-le de plusieurs manières pour voir comment il se comporte :
      • ./mon_outil.sh -h
      • ./mon_outil.sh (sans arguments)
      • ./mon_outil.sh -v -f sortie.txt mon_argument
      • ./mon_outil.sh -x (option invalide)
  2. Créez un outil de “listing” :

    • Adaptez le squelette pour créer un script lister.sh.
    • L’argument obligatoire est un chemin de dossier.
    • Ajoutez une option -a pour “all”, qui passera l’option -a à la commande ls -l.
    • La logique dans main doit simplement exécuter ls -l (ou ls -la si -a est présent) sur le dossier fourni.
  3. Ajoutez une validation :

    • Modifiez lister.sh pour qu’il vérifie si l’argument est bien un dossier qui existe ([[ -d ... ]]).
    • S’il n’existe pas, appelez la fonction die avec un message d’erreur approprié.