// Éditeurs & terminal

Scripts Bash : automatiser son terminal

Un script Bash, c’est une suite de commandes du terminal enregistrée dans un fichier pour être rejouée. Chaque fois que vous répétez trois lignes à la main — sauvegarder un dossier, renommer des fichiers, déployer un projet —, vous avez un candidat à l’automatisation. Bash est le langage de script par défaut sur la plupart des systèmes Linux et macOS : pas d’installation, pas de dépendances, il est déjà là. Ce guide couvre les bases solides pour écrire des scripts fiables plutôt que fragiles.

Le premier script : shebang et exécution

Un script commence par une ligne spéciale, le shebang, qui indique au système quel interpréteur utiliser.

#!/usr/bin/env bash

echo "Bonjour depuis mon premier script"

La forme #!/usr/bin/env bash est préférable au #!/bin/bash codé en dur : elle trouve Bash quel que soit son emplacement dans le système. Enregistrez ce contenu dans bonjour.sh, puis rendez le fichier exécutable et lancez-le :

chmod +x bonjour.sh
./bonjour.sh

Le chmod +x n’est à faire qu’une fois : il ajoute la permission d’exécution au fichier.

Les variables

Une variable stocke une valeur. En Bash, on l’assigne sans espace autour du =, et on la lit en la préfixant d’un $.

#!/usr/bin/env bash

nom="Alice"
dossier="/var/www"

echo "Utilisateur : $nom"
echo "Cible : $dossier"

Deux réflexes importants :

  • Toujours entourer les variables de guillemets doubles ("$dossier") lors de leur usage. Sans cela, un chemin contenant un espace casse la commande.
  • On peut capturer la sortie d’une commande dans une variable avec $( ... ) :
date_du_jour="$(date +%Y-%m-%d)"
nb_fichiers="$(ls -1 | wc -l)"

echo "Nous sommes le $date_du_jour, $nb_fichiers fichiers présents."

Les conditions

Un script devient utile quand il réagit à la situation. La structure if teste une condition ; les crochets doubles [[ ... ]] sont la forme moderne recommandée en Bash.

#!/usr/bin/env bash

fichier="config.yml"

if [[ -f "$fichier" ]]; then
  echo "Le fichier $fichier existe."
else
  echo "Fichier introuvable, création..."
  touch "$fichier"
fi

Les tests les plus courants :

  • -f "$x" : $x est un fichier existant
  • -d "$x" : $x est un dossier
  • -z "$x" : la chaîne est vide
  • "$a" == "$b" : égalité de chaînes
  • "$a" -eq "$b" : égalité numérique

Les boucles

Pour répéter une action sur une liste d’éléments, on utilise for. Exemple : convertir mentalement chaque fichier .txt du dossier.

#!/usr/bin/env bash

for fichier in *.txt; do
  echo "Traitement de $fichier"
  # ... action sur le fichier
done

La boucle while répète tant qu’une condition reste vraie, utile pour lire un fichier ligne par ligne :

while IFS= read -r ligne; do
  echo "Ligne lue : $ligne"
done < liste.txt

Le IFS= read -r préserve les espaces et les antislashs de chaque ligne — un détail qui évite bien des surprises.

Recevoir des arguments

Un script réutilisable ne code pas ses valeurs en dur : il les reçoit en arguments. $1, $2… correspondent aux paramètres passés, $# à leur nombre, $@ à l’ensemble.

#!/usr/bin/env bash

if [[ $# -lt 1 ]]; then
  echo "Usage : $0 <nom-du-dossier>"
  exit 1
fi

cible="$1"
echo "Dossier ciblé : $cible"

Ici, si l’utilisateur oublie l’argument, le script affiche son mode d’emploi et s’arrête avec un code d’erreur 1. $0 contient le nom du script lui-même.

Les fonctions

Quand un bloc de logique se répète, on l’isole dans une fonction. Cela rend le script lisible et réutilisable.

#!/usr/bin/env bash

log() {
  echo "[$(date +%H:%M:%S)] $1"
}

sauvegarder() {
  local source="$1"
  local destination="$2"
  cp -r "$source" "$destination"
  log "Sauvegarde de $source terminée"
}

log "Début du script"
sauvegarder "./projet" "/backup/projet"

Le mot-clé local confine une variable à l’intérieur de la fonction : sans lui, toutes les variables sont globales et se contaminent facilement d’une fonction à l’autre.

Un script utile de bout en bout

Assemblons ces briques dans un script de sauvegarde qui archive un dossier avec la date du jour et vérifie le résultat.

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

source="${1:-}"

if [[ -z "$source" || ! -d "$source" ]]; then
  echo "Usage : $0 <dossier-a-sauvegarder>"
  exit 1
fi

destination="$HOME/backups"
horodatage="$(date +%Y-%m-%d_%H%M%S)"
archive="$destination/$(basename "$source")_$horodatage.tar.gz"

mkdir -p "$destination"
tar -czf "$archive" "$source"

echo "Archive créée : $archive"
echo "Taille : $(du -h "$archive" | cut -f1)"

Lancé avec ./sauvegarde.sh ./mon-projet, il crée une archive compressée horodatée et confirme sa taille. On y retrouve tout ce qui précède : arguments, condition de garde, variables, capture de commande.

Les bonnes pratiques qui évitent les catastrophes

La ligne la plus importante d’un script sérieux est celle-ci, à placer juste après le shebang :

set -euo pipefail

Elle change le comportement par défaut de Bash, plutôt permissif, en un mode strict :

  • -e : le script s’arrête dès qu’une commande échoue, au lieu de continuer aveuglément.
  • -u : utiliser une variable non définie devient une erreur (attrape les fautes de frappe).
  • -o pipefail : dans un pipeline a | b, l’échec de a n’est plus masqué par le succès de b.

Pourquoi c’est vital : sans set -e, un script de déploiement dont la première commande échoue continuera d’exécuter les suivantes sur un état incohérent. Le mode strict transforme un bug silencieux en arrêt net et visible.

Trois autres réflexes complètent ce socle :

  • Guillemets systématiques autour des variables : "$var", jamais $var nu.
  • Vérifier avant de détruire : contrôlez qu’un dossier existe avant un rm -rf, et n’utilisez jamais de variable vide dans un chemin de suppression.
  • Passer le script à ShellCheck, un analyseur qui repère les erreurs classiques :
shellcheck sauvegarde.sh

Automatiser sans se piéger

Bash brille pour la « colle » entre outils : enchaîner des commandes, traiter des fichiers, orchestrer un déploiement. Au-delà d’une centaine de lignes ou dès que la logique se complique (structures de données, calculs), un langage comme Python devient plus confortable. Mais pour tout ce qui reste une suite d’actions du terminal, un script Bash bien écrit — mode strict, variables entre guillemets, fonctions nommées — est l’outil le plus direct. Commencez par automatiser la tâche que vous répétez le plus souvent, et enrichissez au fil des besoins.

À lire ensuite