Retour au cours

subshells

Objectifs

  • Comprendre ce qu’est un subshell (sous-shell) et dans quelles situations il est créé.
  • Utiliser les parenthèses (...) pour exécuter un groupe de commandes dans un environnement isolé.
  • Visualiser l’impact des subshells sur la portée des variables et le répertoire courant.
  • Distinguer le groupement par parenthèses () (subshell) du groupement par accolades {...} (shell courant).

Explications détaillées

Notions clés

Qu’est-ce qu’un subshell ?

Un subshell est un nouveau processus lancé par votre shell principal. C’est une sorte de “clone” temporaire de votre shell.

  • Héritage : Il hérite d’une copie des variables d’environnement du shell parent.
  • Isolation : C’est le point crucial. Toutes les modifications effectuées à l’intérieur du subshell (changement de répertoire avec cd, modification de variable) sont confinées à ce subshell. Une fois le subshell terminé, ces changements sont perdus et le shell parent n’est pas affecté.

Quand un subshell est-il créé ?

Automatiquement dans plusieurs situations courantes :

  1. Parenthèses (...) : C’est la manière la plus explicite de créer un subshell. ( commande1; commande2 )
  2. Pipelines | : Chaque commande dans un pipeline s’exécute dans son propre subshell.
  3. Substitution de commande $(...) : La commande à l’intérieur est exécutée dans un subshell.
  4. Commande en arrière-plan & : La commande est lancée dans un subshell séparé.

() vs {} : Subshell vs Groupement

  • ( ... ) : Les commandes s’exécutent dans un subshell. Les changements sont temporaires.
  • { ...; } : Les commandes sont simplement groupées mais s’exécutent dans le shell courant. Les changements sont persistants. Notez l’espace après { et le point-virgule ; (ou un saut de ligne) avant }.

Syntaxe / Usages

# Lancer des commandes dans un subshell pour isoler les changements
(
  echo "Entrée dans le subshell. PWD: $(pwd)"
  cd /tmp
  echo "Changé de dossier. PWD: $(pwd)"
) # Le subshell se termine ici

# De retour dans le shell parent, le PWD n'a pas changé
echo "Retour dans le parent. PWD: $(pwd)"


# Groupement avec des accolades (pas de subshell)
{
  echo "Début du groupe."
  cd /tmp
  echo "Changé de dossier dans le groupe. PWD: $(pwd)"
} # Le groupe se termine

# Le changement de PWD persiste
echo "Après le groupe. PWD: $(pwd)"

Exemples

# 1. Le piège classique du pipeline
# On veut compter les lignes d'un fichier et stocker le compte
compteur=0
cat mon_fichier.txt | while read -r ligne; do
  ((compteur++))
done
# La boucle 'while' s'exécute dans un subshell. 'compteur' est copié,
# incrémenté dans le subshell, puis détruit.
echo "Nombre de lignes : $compteur" # -> Affiche 0 !

# La solution : éviter le pipe vers 'while'
compteur=0
while read -r ligne; do
  ((compteur++))
done < mon_fichier.txt # Redirection d'entrée, pas de subshell pour la boucle
echo "Nombre de lignes : $compteur" # -> Affiche le bon nombre


# 2. Utiliser un subshell pour des opérations temporaires
# Supposons que nous voulions construire un projet dans un autre dossier
# sans avoir à faire 'cd ..' pour revenir.
echo "Compilation du projet..."
(
  cd ./projet_source/
  ./configure
  make
) # Fin du subshell, nous sommes de retour où nous étions.
echo "Compilation terminée."

Bonnes pratiques

  • Utilisez les subshells avec () de manière intentionnelle pour isoler des actions. C’est parfait pour changer de répertoire temporairement ou pour définir des variables qui ne doivent pas “fuiter” dans le reste du script.
  • Soyez conscient que les pipelines créent des subshells. C’est une source de bugs fréquente pour les débutants qui essaient de modifier des variables dans une boucle while alimentée par un pipe.
  • Préférez la redirection d’entrée (while ... < fichier) à cat fichier | while ... pour éviter le subshell inutile.

Pièges courants

  • Modification de variable dans un pipe : Tenter de modifier une variable dans une boucle while qui fait partie d’un pipeline. La modification sera perdue.
  • Performance : Lancer un subshell a un léger coût (création d’un nouveau processus). Pour des milliers de petites opérations dans une boucle, un groupement {} peut être plus performant, mais seulement si vous n’avez pas besoin de l’isolation.

Exercices

  1. Isolation de cd :

    • Écrivez un script qui :
      1. Affiche le répertoire courant (pwd).
      2. Utilise un subshell () pour se déplacer dans / et lister son contenu.
      3. Affiche à nouveau le répertoire courant pour prouver que le cd était temporaire.
  2. Le compteur qui ne compte pas :

    • Créez un fichier nombres.txt avec 5 lignes.
    • Écrivez un script qui tente de compter les lignes en utilisant cat nombres.txt | while read -r line; do ((count++)); done.
    • Affichez la variable count après la boucle et constatez qu’elle est à 0.
  3. Le compteur qui compte :

    • Corrigez le script de l’exercice précédent en utilisant une redirection d’entrée pour que la boucle while s’exécute dans le shell principal.
    • Vérifiez que la variable count a maintenant la bonne valeur.