Débutant 5 min de lecture · 1 016 mots

Arguments et options en bash : faire de son script une vraie commande

$1, $2, shift, getopts, --help : comment rendre un script configurable sans le modifier à chaque fois, avec un exemple complet de nettoyage de dossier.

Arguments et options en bash : faire de son script une vraie commande
Débutant · 2026.10.15
Estimated reading time: 5 minutes

Au début, on écrit des scripts avec des valeurs en dur : le dossier à sauvegarder, le nombre de jours à conserver, l’adresse du serveur. Ça marche, jusqu’au jour où on veut le réutiliser pour un autre dossier. Alors on copie le script, on change deux lignes, et on se retrouve avec trois versions presque identiques.

La solution : donner des arguments au script. Cet article fait le tour de ce qu’il faut savoir, du plus simple au plus confortable.

Les arguments positionnels

Quand tu lances ./script.sh a b c, bash range ce que tu as tapé dans des variables spéciales :

Variable Contenu
$0 le nom du script
$1, $2, … le premier, le deuxième… argument
$# le nombre d’arguments
"$@" tous les arguments, chacun correctement séparé
$? le code de retour de la dernière commande

Un exemple minimal :

#!/usr/bin/env bash
set -euo pipefail

dossier="${1:?Usage : $0 DOSSIER [JOURS]}"
jours="${2:-30}"

echo "Je nettoierais $dossier (fichiers de plus de $jours jours)"

Deux notations à retenir, très pratiques avec set -u :

  • ${2:-30} : « la valeur de $2, ou 30 si elle est absente ».
  • ${1:?message} : « la valeur de $1, ou arrêter avec ce message si elle est absente ».

C’est suffisant pour un script de deux lignes. Dès qu’il y a plus de deux paramètres, ou des paramètres facultatifs, les arguments positionnels deviennent pénibles : qui se souvient si c’est script.sh 30 /srv ou script.sh /srv 30 ?

getopts : les options courtes (-d, -n)

getopts est intégré à bash. Il lit des options comme -d /srv -j 30 -n. Voici un script complet de nettoyage, avec aide et vérifications.

#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <&2; usage >&2; exit 2 ;;
    ?) echo "Option inconnue : -$OPTARG" >&2; usage >&2; exit 2 ;;
  esac
done
shift $((OPTIND - 1))

[[ -d "$dossier" ]] || { echo "Dossier introuvable : $dossier" >&2; exit 1; }
[[ "$jours" =~ ^[0-9]+$ ]] || { echo "-j attend un nombre entier." >&2; exit 2; }

if (( simulation )); then
  find "$dossier" -type f -mtime +"$jours" -print
else
  find "$dossier" -type f -mtime +"$jours" -print -delete
fi

Comment lire la ligne getopts ":d:j:nh" opt :

  • Le : au tout début active la gestion silencieuse des erreurs : c’est toi qui affiches les messages (cas : et ? dans le case).
  • Une lettre suivie de : (d:, j:) attend une valeur, disponible dans $OPTARG.
  • Une lettre seule (n, h) est un interrupteur.

Après la boucle, shift $((OPTIND - 1)) retire les options traitées : il ne reste dans $@ que les arguments « normaux » (par exemple une liste de fichiers).

Quelques essais :

./nettoyer.sh -h                     # affiche l'aide
./nettoyer.sh -n                     # simulation avec les valeurs par défaut
./nettoyer.sh -d /srv/photos -j 90 -n

Le mode simulation (-n) est le réflexe le plus utile pour un script qui supprime ou déplace des choses : on voit ce qui serait fait, avant de le faire pour de vrai.

Les options longues (--dossier, --dry-run)

getopts ne gère pas les options longues. Quand on en veut, on écrit soi-même la boucle, avec case et shift :

#!/usr/bin/env bash
set -euo pipefail

dossier="/srv/sauvegardes"
jours=30
simulation=0

while (( $# > 0 )); do
  case "$1" in
    -d|--dossier)  dossier="${2:?--dossier attend une valeur}"; shift 2 ;;
    -j|--jours)    jours="${2:?--jours attend une valeur}"; shift 2 ;;
    -n|--dry-run)  simulation=1; shift ;;
    -h|--help)     echo "Usage : $0 [--dossier D] [--jours N] [--dry-run]"; exit 0 ;;
    --)            shift; break ;;
    -*)            echo "Option inconnue : $1" >&2; exit 2 ;;
    *)             break ;;
  esac
done

echo "dossier=$dossier jours=$jours simulation=$simulation reste=$*"

Ce que fait chaque shift : il fait « avancer » la liste d’arguments. shift 2 retire l’option et sa valeur. À la fin, $@ contient ce qui n’a pas été reconnu comme une option.

C’est un peu plus long que getopts, mais c’est lisible, et ça ne dépend d’aucun outil externe.

Lire une valeur depuis l’environnement

Un troisième canal, très utile pour les secrets ou les réglages qui changent peu : les variables d’environnement.

NTFY_URL="${NTFY_URL:-}"      # vide si non défini
if [[ -n "$NTFY_URL" ]]; then
  curl -fsS -d "Script terminé" "$NTFY_URL" >/dev/null
fi

On les passe ainsi : NTFY_URL=https://ntfy.sh/mon-canal ./script.sh. L’avantage : un mot de passe ou un jeton passé en argument est visible par tous les utilisateurs de la machine dans la liste des processus (ps), alors qu’une variable d’environnement l’est beaucoup moins. Pour un jeton, évite donc l’argument de ligne de commande.

Les codes de retour : parler aux autres programmes

Un script bien élevé renvoie un code de retour :

  • 0 : tout s’est bien passé ;
  • 1 : erreur « générale » (le travail a échoué) ;
  • 2 : mauvais usage (option inconnue, argument manquant), par convention.

C’est ce que lisent cron, systemd, et les scripts qui t’appellent :

if ./nettoyer.sh -n -d /srv/photos; then
  echo "ok"
else
  echo "échec (code $?)"
fi

Récapitulatif : que choisir ?

Besoin Outil
Un ou deux paramètres obligatoires $1, $2 avec ${1:?…}
Des paramètres facultatifs avec valeurs par défaut ${2:-valeur}
Plusieurs options courtes getopts
Options longues (--dry-run) boucle while + case + shift
Secrets, réglages stables variables d’environnement

Dans le prochain article, on utilise tout ça pour écrire un vrai script de sauvegarde avec rsync.

Une remarque, un retour ?

Cet article est vivant - corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisis celui qui te convient.

Laisser un commentaire