Aller au contenu principal

Des options en ligne de commande avec getopts

Notions théoriques

Options et arguments positionnels

Jusqu'ici, sauvegarde.sh reçoit ses dossiers directement en arguments positionnels ($1, $2...). Mais la plupart des commandes Unix (ls -la, tar -czf archive.tar.gz dossier) distinguent deux choses :

  • les options, introduites par un tiret (-v, -h, -d /tmp), qui modifient le comportement de la commande et peuvent apparaître dans n'importe quel ordre ;
  • les arguments positionnels, le « sujet » réel de la commande (le dossier à sauvegarder).

Bash fournit une commande interne dédiée à l'analyse de ce type d'options : getopts.

La boucle getopts

while getopts "hvd:" opt; do
case "$opt" in
h) usage; exit 0 ;;
v) verbeux=1 ;;
d) destination="$OPTARG" ;;
\?) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))

Remarquez que le traitement de chaque option repose directement sur le case vu en séance 1.

  • La chaîne "hvd:" liste les lettres d'options acceptées : h, v et d.
  • Une lettre seule (h, v) est une option booléenne : présente ou absente.
  • Une lettre suivie de : (d:) signifie que l'option attend une valeur, disponible ensuite dans la variable spéciale $OPTARG.
  • $OPTIND est mis à jour à chaque itération : c'est l'indice du prochain argument à traiter, options comprises.
attention

Après la boucle, shift $((OPTIND - 1)) est indispensable : il décale $1, $2... pour que "$@" ne contienne plus que les arguments positionnels (les dossiers), sans les options déjà traitées. Sans ce shift, $1 continuerait de pointer vers -v au lieu du premier dossier.

Gérer les erreurs de saisie

while getopts ":hvd:" opt; do
case "$opt" in
h) usage; exit 0 ;;
v) verbeux=1 ;;
d) destination="$OPTARG" ;;
\?) echo "Option inconnue : -$OPTARG" >&2; usage; exit 1 ;;
:) echo "L'option -$OPTARG nécessite une valeur" >&2; exit 1 ;;
esac
done
  • La branche \?) capte toute option inconnue (non listée dans la chaîne d'options).
  • En plaçant un : en tout début de la chaîne d'options (":hvd:"), getopts passe en mode silencieux et permet de détecter vous-même le cas d'une option qui attend une valeur mais n'en a pas reçu, via la branche :). Dans ce mode, $OPTARG contient alors la lettre d'option fautive.

Valeurs par défaut

Une option n'étant par nature pas obligatoire, il est indispensable de fixer une valeur par défaut avant la boucle getopts, au cas où l'utilisateur ne la fournit pas :

verbeux=0
destination="/tmp/sauvegardes"

while getopts "hvd:" opt; do
...
done

Une utilisation typique du drapeau verbeux est de conditionner l'affichage de messages supplémentaires avec une fonction dédiée :

log_verbose() {
if [ "$verbeux" -eq 1 ]; then
echo "[verbose] $1"
fi
}
info

getopts ne gère pas les options « longues » façon --verbose ou --destination=/tmp : c'est une limitation connue, propre à la commande interne de Bash. Pour ce besoin, les scripts plus élaborés utilisent la commande externe getopt (du paquet util-linux) ou analysent "$@" manuellement avec une boucle while [ $# -gt 0 ]. Nous nous limitons ici aux options courtes, largement suffisantes pour un script d'administration courant.

Exemple pratique

#!/bin/bash
# Script : rapport.sh
# Rôle : générer un rapport, avec options -h, -v et -o

usage() {
echo "Usage : $0 [-h] [-v] [-o fichier] <dossier>"
}

verbeux=0
sortie="/tmp/rapport.txt"

while getopts ":hvo:" opt; do
case "$opt" in
h) usage; exit 0 ;;
v) verbeux=1 ;;
o) sortie="$OPTARG" ;;
\?) echo "Option inconnue : -$OPTARG" >&2; usage; exit 1 ;;
:) echo "L'option -$OPTARG nécessite une valeur" >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1))

if [ $# -eq 0 ]; then
usage
exit 1
fi

dossier="$1"

if [ "$verbeux" -eq 1 ]; then
echo "[verbose] Dossier analysé : $dossier"
echo "[verbose] Fichier de sortie : $sortie"
fi

echo "Rapport pour $dossier" > "$sortie"
echo "Rapport généré : $sortie"

Exécution : ./rapport.sh -h, ./rapport.sh -v -o /tmp/rapport.txt /var/log.

Test de mémorisation/compréhension


Que signifie le : après la lettre d dans la chaîne "hvd:" passée à getopts ?


Dans quelle variable getopts place-t-il la valeur d'une option ?


Pourquoi écrit-on shift $((OPTIND - 1)) après la boucle getopts ?


Que capte la branche \?) dans le case d'une boucle getopts ?


TP pour réfléchir et résoudre des problèmes

Nous transformons sauvegarde.sh en un vrai outil en ligne de commande, avec des options -h, -d et -v.

Étape 1 — Ajouter l'option -h


Bonne pratique - Toujours prévoir -h

Une option -h (ou --help) qui affiche l'usage et sort avec le code 0 devrait être systématique sur tout script destiné à être réutilisé, y compris par vous-même dans six mois.

Étape 2 — Ajouter l'option -d avec valeur par défaut


Bonne pratique - Une valeur par défaut avant la boucle

Fixez toujours la valeur par défaut d'une option avant la boucle getopts : si l'utilisateur ne fournit pas -d, le script doit rester utilisable avec un comportement raisonnable.

Étape 3 — Ajouter le mode verbeux et gérer les erreurs


Bonne pratique - Ne jamais ignorer une option inconnue

Un script qui ignore silencieusement une option mal orthographiée (-x au lieu de -v) peut donner l'illusion que l'option a été prise en compte. Toujours signaler l'erreur et arrêter l'exécution.

Étape 4 — Décaler les arguments et récupérer les dossiers


Bonne pratique - Toujours shift avant de lire "$@"

Sans le shift $((OPTIND - 1)) de l'étape précédente, "$@" contiendrait encore les options déjà traitées (-v, -d, /tmp/sauvegardes) mélangées aux vrais dossiers à sauvegarder.

📌 Une solution