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